# -*- coding: iso-8859-1 -*-
"""
    MoinMoin - Form Parser

    @copyright: 2006 Canonical <matthew.nuzum@canonical.com>
    @license: GNU GPL, see COPYING for details.

"""

from MoinMoin import config, wikiutil

Dependencies = []

class Parser:
    """
        Display a form that conforms to that described via user generated markup
    """

    ## specify extensions willing to handle (for inline:)
    ## should be a list of extensions including the leading dot
    ## TODO: remove the leading dot from the extension. This is stupid.
    #extensions = ['.txt']
    ## use '*' instead of the list(!) to specify a default parser
    ## which is used as fallback
    extensions = '*'
    Dependencies = []
    
    def __init__(self, raw, request, **kw):
        self.raw = raw
        self.request = request
        self.form = request.form
        self._ = request.getText
        self.__inSection = False
        self.__form_details = {}
        self._form_element_count = 0
        self._form_required_default = False
        self._allowed_atributes = {
                 'form': ["action","method","enctype","name","id"],
                 'text': ["name","id","length","class","value"],
                 'password': ["name","id","length","class","value"],
                 'email': ["name","id","length","class","value"],
                 'url': ["name","id","length","class","value"],
                 'select': ["name","id","class"],
                 'textarea': ["name","id","length","class","rows","cols"],
                 'hidden': ["name","id"],
                 'checkbox': [],
                 'radio': []
                                 }
        self._css_section = "moin_form_section"
        self._css_label = "moin_form_label"
        self._css_text = "moin_form_text"
        self._css_url = "moin_form_text"
        self._css_email = "moin_form_text"
        self._css_password = "moin_form_text"
        self._css_textarea = "moin_form_textarea"
        self._css_checkbox = "moin_form_checkbox"
        self._css_checkboxgroup = "moin_form_checkboxgroup"
        self._css_radio = "moin_form_radio"
        self._css_radiogroup = "moin_form_radiogroup"
        self._css_select = "moin_form_select"
        self._css_buttonlabel = "moin_form_buttonlabel"

    def format(self, formatter):
        """ Display the form. """
        # separate our form into lines, then call the necessary function for each line
        lines = self.raw.split('\n')
        
        if lines[0][0:4] == "form":
            # get the definition for the form
            form_def = lines[0].split('|')
            # data 1 and 5 are key=value sets
            if(len(form_def) > 1 and form_def[1]):
                form_def[1] = self.dataum_to_dict(form_def[1])
        else:
            form_def = ["form",{"method":"get"},"form1",""]
        self.__form_details["name"] = "form1"
        self.__form_details["id"] = "form1"
        self.__form_details["method"] = "get"
        self.__form_details["action"] = ""
        self.__form_details["enctype"] = "multipart/form-data"
        self.__form_details["cancel"] = "f"
        self.__form_details["cancel_text"] = "Cancel"
        self.__form_details["submit_text"] = "Submit"
        self.__form_details["required_default"] = "f"
        for key,value in form_def[1].iteritems():
            self.__form_details[key] = value
        
        # sanitize certain values
        self.__form_details["cancel"] = self.to_boolean(self.__form_details["cancel"])
        self._form_required_default = self.to_boolean(self.__form_details["required_default"])
        
        if(len(form_def) > 2 and form_def[2]): 
            self.__form_details["name"] = form_def[2]
        if(len(form_def) > 3 and form_def[3]): 
            self.__form_details["action"] = form_def[3]
        
        #self.request.write(formatter.preformatted(1))
        self.request.write('<div class="moin_form"><form')
        for key,value in self.__form_details.iteritems():
            try:
                if self._allowed_atributes["form"].index(key) >= 0:
                    self.request.write(' %s="%s"' % (key,value))
            except ValueError:
                pass
        self.request.write(' onsubmit="return %s_validateForm(this)"' % self.__form_details["name"])
        self.request.write('>\n')
        self.request.write("""<script type="text/javascript">
        var %s_requiredFields = new Array();
        function %s_validateForm(formObj) {
            var error = '';
            for (i = 0; i < %s_requiredFields.length;i++) {
                type = %s_requiredFields[i][0];
                id = %s_requiredFields[i][1];
                label = %s_requiredFields[i][2];
                extra = %s_requiredFields[i][3];
                elemObj = document.getElementById(id);
                if(type == 'text') {
                    if(elemObj.value == '') {
                        error += "\\n* \\""+label+"\\" cannot be blank"
                    }
                } else if(type == 'url') {
                    if(elemObj.value = '') {
                        error += "\\n* \\""+label+"\\" cannot be blank"
                    } else if(elemObj.value.indexOf(".") > 2 && elemObj.value.indexOf("@") > 0) {
                        error += "\\n* \\""+label+"\\" must be an e-mail address"
                    }
                
                } else if(type == 'email') {
                    if(elemObj.value = '') {
                        error += "\\n* \\""+label+"\\" cannot be blank"
                    } else if(elemObj.value.indexOf(".") > 2 && elemObj.value.indexOf("@") > 0) {
                        error += "\\n* \\""+label+"\\" must be an e-mail address"
                    }
                
                } else if(type == 'mustmatch') {
                    matchObj = document.getElementById(extra);
                    if(elemObj.value != matchObj.value) {
                        error += "\\n* \\""+label+"\\" must match "+extra;
                    }
                }
            }
            
            if (error != '') {
                alert(error);
                return false;
            }
            return true;
            
        }
        </script>
        """ % (self.__form_details["name"],self.__form_details["name"],
               self.__form_details["name"],self.__form_details["name"],
               self.__form_details["name"],self.__form_details["name"],
               self.__form_details["name"]))
        
        for line in lines[1:]:
            line = wikiutil.escape(line, True)
            data = line.split('|')
            
            # data 1 and 5 are key=value sets
            if len(data) > 1 and data[1]:
                data[1] = self.dataum_to_dict(data[1])
            else:
                data[1] = {}
            if not len(data) > 2:
                data.append("")
            if not len(data) > 3:
                data.append("")
            if not len(data) > 4:
                data.append("")
            if len(data) > 5 and data[5]:
                data[5] = self.dataum_to_dict(data[5])
            else:
                data.append({})
            if not len(data) > 6:
                data.append("")
            
            # setup some of our default values that every element uses
            if len(data) > 3 and data[3]:
                data[1]["label"] = data[3]
            else: 
                data[1]["label"] = ""
            
            if len(data) > 2 and data[2]:
                data[1]["name"] = data[2]
            else:
                data[1]["name"] = 'field%d' % self._form_element_count
            
            if not data[1].has_key("id"):
                data[1]["id"] = data[1]["name"]
                
            if data[1].has_key("required"):
                data[1]["required"] = self.to_boolean(data[1]["required"])
            else:
                # this is configurable - form fields can either be required
                # or not required by default and then overriden per field
                data[1]["required"] = self._form_required_default
            
            try:
                getattr(self, "display_"+data[0])(data)
                self._form_element_count = self._form_element_count + 1 
                
            except AttributeError:
                self.request.write("<!-- Cannot process this form element type -->\n")
        
        # end a section if one was begun
        if self.__inSection:
            self.request.write("</fieldset>")
        
        # show submit and cancel buttons
        self.request.write("<div>")
        if(self.__form_details["cancel"]):
            self.request.write('<input type="button" value="%s" onclic="history.go(-1)" />' % self.__form_details["cancel_text"])
        self.request.write('<input type="submit" value="%s" /></div></form></div>\n' % self.__form_details["submit_text"])
    
    def display_section(self, data):
        """ display a new section """
        if self.__inSection == True:
            self.request.write("</fieldset>\n")
        self.__inSection = True
        self.request.write('<fieldset class="%s">' % self._css_section)
        if len(data) > 3 and data[3]:
            self.request.write('<legend class="%s">%s</legend>' % (self._css_section, data[3]))
        if len(data) > 6 and data[6]:
            self.request.write('<p>%s</p>' % data[6])
        self.request.write('\n')
    
    def display_text(self, data):
        """ display a plain text field """
        if not data[1].has_key("class"):
            data[1]["class"] = self._css_text
        if data[1]["required"]: label_class = "required"
        else: label_class = ""
        self.request.write('<div><label for="%s" class="%s">' % (data[1]["id"], label_class))
        self.request.write(data[1]["label"])
        self.request.write('</label> <input type="text"')
        for key,value in data[1].iteritems():
            try:
                if self._allowed_atributes["text"].index(key) >= 0:
                    self.request.write(' %s="%s"' % (key,value))
            except ValueError:
                pass
        self.request.write('/>')
        if data[1]["required"]:
            self.request.write('<span class="required">*</span>')
            self.request.write("""<script type="text/javascript">
            var i = %s_requiredFields.length
            %s_requiredFields[i] = new Array();
            %s_requiredFields[i][0] = 'text';
            %s_requiredFields[i][1] = '%s';
            %s_requiredFields[i][2] = unescape('%s');
            </script>""" % (self.__form_details["name"],self.__form_details["name"],
                            self.__form_details["name"],self.__form_details["name"],
                            data[1]["id"],self.__form_details["name"],
                            data[1]["label"].replace("'","\\'")))
        if len(data) > 6 and data[6]:
            self.request.write('<p>%s</p>' % data[6])
        self.request.write('</div>\n')
    
    def display_email(self, data):
        """ display an email text field """
        if not data[1].has_key("class"):
            data[1]["class"] = self._css_email
        if data[1]["required"]: label_class = "required"
        else: label_class = ""
        self.request.write('<div><label for="%s" class="%s">' % (data[1]["id"], label_class))
        self.request.write(data[1]["label"])
        self.request.write('</label> <input type="text"')
        for key,value in data[1].iteritems():
            try:
                if self._allowed_atributes["email"].index(key) >= 0:
                    self.request.write(' %s="%s"' % (key,value))
            except ValueError:
                pass
        self.request.write('/>')
        if data[1]["required"]:
            self.request.write('<span class="required">*</span>')
            self.request.write("""<script type="text/javascript">
            var i = %s_requiredFields.length
            %s_requiredFields[i] = new Array();
            %s_requiredFields[i][0] = 'text';
            %s_requiredFields[i][1] = '%s';
            %s_requiredFields[i][2] = unescape('%s');
            </script>""" % (self.__form_details["name"],self.__form_details["name"],
                            self.__form_details["name"],self.__form_details["name"],
                            data[1]["id"],self.__form_details["name"],
                            data[1]["label"].replace("'","\\'")))
        if len(data) > 6 and data[6]:
            self.request.write('<p>%s</p>' % data[6])
        self.request.write('</div>\n')
    
    def display_url(self, data):
        """ display an url text field """
        if not data[1].has_key("class"):
            data[1]["class"] = self._css_url
        if data[1]["required"]: label_class = "required"
        else: label_class = ""
        self.request.write('<div><label for="%s" class="%s">' % (data[1]["id"], label_class))
        self.request.write(data[1]["label"])
        self.request.write('</label> <input type="text"')
        for key,value in data[1].iteritems():
            try:
                if self._allowed_atributes["url"].index(key) >= 0:
                    self.request.write(' %s="%s"' % (key,value))
            except ValueError:
                pass
        self.request.write('/>')
        if data[1]["required"]:
            self.request.write('<span class="required">*</span>')
            self.request.write("""<script type="text/javascript">
            var i = %s_requiredFields.length
            %s_requiredFields[i] = new Array();
            %s_requiredFields[i][0] = 'text';
            %s_requiredFields[i][1] = '%s';
            %s_requiredFields[i][2] = unescape('%s');
            </script>""" % (self.__form_details["name"],self.__form_details["name"],
                            self.__form_details["name"],self.__form_details["name"],
                            data[1]["id"],self.__form_details["name"],
                            data[1]["label"].replace("'","\\'")))
        if len(data) > 6 and data[6]:
            self.request.write('<p>%s</p>' % data[6])
        self.request.write('</div>\n')
    
    def display_hidden(self, data):
        """ display an hidden text field """
        self.request.write('<input type="hidden"')
        for key,value in data[1].iteritems():
            try:
                if self._allowed_atributes["hidden"].index(key) >= 0:
                    self.request.write(' %s="%s"' % (key,value))
            except ValueError:
                pass
        if data[4]:
            self.request.write(' value="%s"' % data[4])
        self.request.write('/>\n')
    
    def display_password(self, data):
        """ display an password text field """
        if not data[1].has_key("class"):
            data[1]["class"] = self._css_password
        if data[1]["required"]: label_class = "required"
        else: label_class = ""
        self.request.write('<div><label for="%s" class="%s">' % (data[1]["id"], label_class))
        self.request.write(data[1]["label"])
        self.request.write('</label> <input type="password"')
        for key,value in data[1].iteritems():
            try:
                if self._allowed_atributes["password"].index(key) >= 0:
                    self.request.write(' %s="%s"' % (key,value))
            except ValueError:
                pass
        self.request.write('/>')
        if data[1]["required"]:
            self.request.write('<span class="required">*</span>')
            self.request.write("""<script type="text/javascript">
            var i = %s_requiredFields.length
            %s_requiredFields[i] = new Array();
            %s_requiredFields[i][0] = 'text';
            %s_requiredFields[i][1] = '%s';
            %s_requiredFields[i][2] = unescape('%s');
            </script>""" % (self.__form_details["name"],self.__form_details["name"],
                            self.__form_details["name"],self.__form_details["name"],
                            data[1]["id"],self.__form_details["name"],
                            data[1]["label"].replace("'","\\'")))
        if len(data) > 6 and data[6]:
            self.request.write('<p>%s</p>' % data[6])
        self.request.write('</div>\n')
    
    def display_textarea(self, data):
        """ display an textarea """
        if not data[1].has_key("class"):
            data[1]["class"] = self._css_textarea
        if data[1]["required"]: label_class = "required"
        else: label_class = ""
        if not data[1].has_key("rows"): data[1]["rows"] = "4"
        if not data[1].has_key("cols"): data[1]["cols"] = "15"
        self.request.write('<div><label for="%s" class="%s">' % (data[1]["id"], label_class))
        self.request.write(data[1]["label"])
        self.request.write('</label>')
        if data[1]["required"]:
            self.request.write('<span class="required">*</span>')
            self.request.write("""<script type="text/javascript">
            var i = %s_requiredFields.length
            %s_requiredFields[i] = new Array();
            %s_requiredFields[i][0] = 'text';
            %s_requiredFields[i][1] = '%s';
            %s_requiredFields[i][2] = unescape('%s');
            </script>""" % (self.__form_details["name"],self.__form_details["name"],
                            self.__form_details["name"],self.__form_details["name"],
                            data[1]["id"],self.__form_details["name"],
                            data[1]["label"].replace("'","\\'")))
        self.request.write('<textarea')
        for key,value in data[1].iteritems():
            try:
                if self._allowed_atributes["textarea"].index(key) >= 0:
                    self.request.write(' %s="%s"' % (key,value))
            except ValueError:
                pass
        self.request.write('>')
        self.request.write("") # default value
        self.request.write('</textarea>')
        if len(data) > 6 and data[6]:
            self.request.write('<p>%s</p>' % data[6])
        self.request.write('</div>\n')
    
    def display_select(self, data):
        """ display an select field """
        if not data[1].has_key("class"):
            data[1]["class"] = self._css_select
        if data[1]["required"]: label_class = "required"
        else: label_class = ""
        self.request.write('<div><label for="%s" class="%s">' % (data[1]["id"], label_class))
        self.request.write(data[1]["label"])
        self.request.write('</label>  <select')
        for key,value in data[1].iteritems():
            try:
                if self._allowed_atributes["select"].index(key) >= 0:
                    self.request.write(' %s="%s"' % (key,value))
            except ValueError:
                pass
        self.request.write('>')
        for key,value in data[5].iteritems():
            self.request.write('\t<option value="%s">%s</option>\n' % (key,value))
        self.request.write('</select>')
        if len(data) > 6 and data[6]:
            self.request.write('<p>%s</p>' % data[6])
        self.request.write('</div>\n')
    
    def display_radio(self, data):
        """ display an radio button """
        if not data[1].has_key("class"):
            data[1]["class"] = self._css_radio
        self.request.write('<div><fieldset class="%s"><legend>%s</legend>'
                            % (self._css_radiogroup, data[1]["label"]))
        for key,value in data[5].iteritems():
            self.request.write('\t<label><input type="radio" name="%s" value="%s" class="%s"/>' % 
                               (data[1]["name"], key, data[1]["class"]))
            self.request.write('%s</label>\n' % value)
        if len(data) > 6 and data[6]:
            self.request.write('<p>%s</p>' % data[6])
        self.request.write('</fieldset>')
        self.request.write('</div>\n')
    
    def display_checkbox(self, data):
        """ display an checkbox """
        if not data[1].has_key("class"):
            data[1]["class"] = self._css_radio
        self.request.write('<div><fieldset class="%s"><legend>%s</legend>'
                            % (self._css_checkboxgroup, data[1]["label"]))
        for key,value in data[5].iteritems():
            self.request.write('\t<label><input type="checkbox" name="%s" value="%s" class="%s" />' % 
                               (data[1]["name"], key, data[1]["class"]))
            self.request.write('%s</label>\n' % value)
        if len(data) > 6 and data[6]:
            self.request.write('<p>%s</p>' % data[6])
        self.request.write('</fieldset>')
        self.request.write('</div>\n')
    
    def dataum_to_dict(self,datum):
        """ convert a string such as abc=123;xyz=789 to {"abc": "123", "xyz": "789"} """
        res = {}
        pairs = datum.split(";")
        for pair in pairs:
            pair = pair.split('=')
            if len(pair) > 1:
                res[pair[0]] = pair[1]
            elif len(pair) == 1:
                res[pair[0]] = pair[0]
        return res
    
    def to_boolean(self,value):
        """ convert the value into a boolean """
        val = value.lower() 
        try:
            if val[0] == "t" or val[0] == "y" or val == 1: return True
        except:
            pass
        return False
        
