# This file contains a library of subclasses of Field
# These are the actual field classes you'll use in your work

from BaseFieldClasses import Field, CompoundField, ChooseField

from Validators import NotEmpty

class TextField(Field):
    
    def tag(self):
        if self.languageStandard() == 'XHTML':
            xhtml = " /"
        else:
            xhtml = ""
        return '''<input type="text" id="%s" name="%s" value="%s"%s%s>''' % (
            self._generateID(),
            self.name(),
            self._valueForDisplay(),
            self._tagAttributes(),
            xhtml )

class TextAreaField(Field):

    def tag(self):
        return '''<textarea id="%s" name="%s" %s>%s</textarea>''' % (
            self._generateID(),
            self.name(),
            self._tagAttributes(),
            self._valueForDisplay(), )

class TextInputField(Field):
    '''This field class provides a generalized text input interface and manifests either as a
    text field or a textarea field, depending on the maximum length string requested'''

    def __init__(self, name, validators=[], label='', attributes={}, languageStandard='XHTML', maxLength=0, rows=None, cols=None):

        Field.__init__(self, name, validators, label, attributes, languageStandard)
        self._maxLength = maxLength
        self._rows = rows
        self._cols = cols

    def tag(self):
        if self._maxLength > 0 and self._maxLength < 256:
            if self.languageStandard() == 'XHTML':
                xhtml = " /"
            else:
                xhtml = ""
            return '''<input type="text" id="%s" name="%s" maxlength="%s" value="%s"%s%s>''' % (
                self._generateID(),
                self.name(),
                self._maxLength,
                self._valueForDisplay(),
                self._tagAttributes(),
                xhtml )
        else:
            # rows and columns for textarea
            if self._rows and self._cols:
                dims = ''' rows="%s" cols="%s"''' % (self._rows,self._cols)
            else:
                dims = ''

            return '''<textarea id="%s" name="%s" %s%s>%s</textarea>''' % (
                self._generateID(),
                self.name(),
                self._tagAttributes(),
                dims,
                self._valueForDisplay(), )


class HiddenField(Field):
    # these tags should probably be queued up somehow and output at the end of the form, just before the </form> tag
    
    def tag(self):
        if self.languageStandard() == 'XHTML':
            xhtml = " /"
        else:
            xhtml = ""
        return '''<input type="hidden" id="%s" name="%s" value="%s"%s%s>''' % (
            self._generateID(),
            self.name(),
            self._valueForDisplay(),
            self._tagAttributes(),
            xhtml )

class PasswordField(Field):
    
    def tag(self):
        if self.languageStandard() == 'XHTML':
            xhtml = " /"
        else:
            xhtml = ""
        return '''<input type="password" id="%s" name="%s" value="%s"%s%s>''' % (
            self._generateID(),
            self.name(),
            self._valueForDisplay(),
            self._tagAttributes(),
            xhtml )

class FileField(Field):
    
    def tag(self):
        if self.languageStandard() == 'XHTML':
            xhtml = " /"
        else:
            xhtml = ""
        return '''<input type="file" id="%s" name="%s"%s%s>''' % (
            self._generateID(),
            self.name(),
            self._tagAttributes(),
            xhtml )

    def _requireMultipartEncoding(self):
        # this method triggers multipart/form encoding mode for uploading files
        # it need only be defined for fields of type 'file'
        return 1


class SelectField(ChooseField):
    '''Select box field'''
    def tag(self):
        '''Public method to return the HTML tags for this field'''
        options = ''
        if self.choices():
            for each in self.choices():
                options += each.tag()

        return '''<select id="%s" name="%s"%s>%s\n</select>''' % (
            self._generateID(),
            self.name(),
            self._tagAttributes(),
            options )

    def setChoices(self,choiceListObject):
        '''Pass a selectionList object to this field object to set its selection set'''
        # call the super
        ChooseField.setChoices(self,choiceListObject)
        # tell the ChoiceList what its type is going to be
        self._choiceList.setType( 'select' )


class MultiSelectField(SelectField):

    def tag(self):
        '''Public method to return the HTML tags for this field'''
        options = ''
        if self.choices():
            for each in self.choices():
                options += each.tag()
        else:
            options = ''

        if self.languageStandard() == 'XHTML':
            multipleStr = ' multiple="multiple"'
        else:
            multipleStr = ' multiple'

        # if there is no size in the attributes, by default set the size to the number of choices
        if not self.attributes().has_key('size'):
            self.addAttributes({'size':len(self.choices())})
                
        return '''<select id="%s" name="%s"%s%s>%s\n</select>''' % (
            self._generateID(),
            self.name(),
            multipleStr,
            self._tagAttributes(),
            options )


class RadioSet(ChooseField):

    def __init__(self, name, validators=[], label='', attributes={}, languageStandard='XHTML'):
        ChooseField.__init__(self, name, validators, label, attributes, languageStandard)
        self._choiceList.setType('radio')
        
    def tag(self):
        '''Public method to return the HTML tags for this field'''
        options = ''
        if self.choices():

            for choice in self.choices():
                options += '\n\t%s %s<br%s>' % ( choice.tag(), choice.label(), self._closeTag() )

        return options

    def setChoices(self,choiceListObject):
        '''Pass a selectionList object to this field object to set its selection set'''
        ChooseField.setChoices( self, choiceListObject)
        
        self._choiceList.setType( 'radio' )     # tell the ChoiceList what its type is going to be


class CheckboxSet(ChooseField):
    def __init__(self, name, validators=[], label='', attributes={}, languageStandard='XHTML'):
        ChooseField.__init__(self, name, validators, label, attributes, languageStandard)
        self._choiceList.setType( 'checkbox' )

    def tag(self):
        '''Public method to return the HTML tags for this field'''
        options = ''
        if self.choices():
            for choice in self.choices():
                options += '\n\t%s %s<br%s>' % ( choice.tag(), choice.label(), self._closeTag() )

        return options

    def setChoices(self,choiceListObject):
        '''Pass a selectionList object to this field object to set its selection set'''
        ChooseField.setChoices( self, choiceListObject)
        
        self._choiceList.setType( 'checkbox' )      # tell the ChoiceList what its type is going to be


class SubmitButton(Field):

    def tag(self):

        if self.languageStandard() == 'XHTML':
            return '''<input type="submit" id="%s" value="%s" %s />''' % (
                self._generateID(),
                self.label(),
                self._tagAttributes())
        else:
            return '''<input type="submit" id="%s" value="%s" %s>''' % (
                self._generateID(),
                self.label(),
                self._tagAttributes())


class ResetButton(Field):

    def tag(self):

        if self.languageStandard() == 'XHTML':
            return '''<input type="reset" id="%s" %s />''' % (
                self._generateID(),
                self._tagAttributes())
        else:
            return '''<input type="reset" id="%s" %s>''' % (
                self._generateID(),
                self._tagAttributes())

class Button(Field):

    def tag(self):

        if self.languageStandard() == 'XHTML':
            xhtml = " /"
        else:
            xhtml = ""

        return '''<input type="button" id="%s" name="%s" value="%s" %s%s>''' % (
            self._generateID(),
            self.name(),
            self._valueForDisplay(),
            self._tagAttributes(),
            xhtml)


class ImageButton(Field):
    '''This is an Image Submit button. This is not for use with webkit: use WebKitImageButton() for that.'''
    # Image buttons work just like html buttons, but they pass in the coordinates that the user clicked on
    def setImage(self, url, width=None, height=None):
        if url:
            self.addAttributes( {'src': url } )
        if width:
            self.addAttributes( {'width' : width } )
        if height:
            self.addAttributes( {'height' : height } )

    def tag(self):
        if self.languageStandard() == 'XHTML':
            xhtml = " /"
        else:
            xhtml = ""
        return '''<input type="image" id="%s" name="%s" value="%s" %s%s>''' % (
            self._generateID(),
            self.name(),
            self._valueForDisplay(),
            self._tagAttributes(),
            xhtml)

class WebKitSubmitButton(SubmitButton):
    '''This is a special submit button that is configured for use with Webware'''

    ## NB: this only works with WebKit 0.8 and later

    # Webware WebKit requires that button names be prefixed with "_action_" in order to associate
    #   them with servlet methods

    def _findSelfInDict(self, dict):
        return dict.get( '_action_%s' % self.name() )

    def tag(self):
        if self.languageStandard() == 'XHTML':
            xhtml = " /"
        else:
            xhtml = ""

        return '''<input type="submit" id="%s" name="_action_%s" value="%s"%s%s>''' % (
            self._generateID(),
            self.name(),
            self.label(),
            self._tagAttributes(),
            xhtml )


class WebKitImageButton(ImageButton):
    '''This is a special image submit button that is configured for use with Webware'''
    # Image buttons work just like html buttons, but they pass in the coordinates that the user clicked on

    def _findSelfInDict(self, dict):
        try:
            x = int(dict.get( '_action_%s.x' % self.name() ))
            y = int(dict.get( '_action_%s.y' % self.name() ))
            return (x,y)
        except ValueError:
            # some error in int() the values
            return None

    def tag(self):
        if self.languageStandard() == 'XHTML':
            xhtml = " /"
        else:
            xhtml = ""
        return '''<input type="image" id="%s" name="_action_%s" value="%s" %s%s>''' % (
            self._generateID(),
            self.name(),
            self._valueForDisplay(),
            self._tagAttributes(),
            xhtml)

class EditableSelectField(CompoundField,SelectField):
    '''This compound field class simulates and editable select box by using two fields:
    a select box and a text field'''

    def __init__(self, name, validators=[], label='', attributes={}, languageStandard='XHTML'):
        SelectField.__init__(self, name, validators, label, attributes, languageStandard )
        CompoundField.__init__(self, name, validators, label, attributes, languageStandard )

        # add a sub-field that will be the type-in box for the new choice
        typeInName= self.name() + '_typein'
        self.addSubField( TextField( typeInName ) )

    def tag(self):
        '''custom tag method'''
        cluster = ''

        return SelectField.tag(self) + ' or input new: ' + self._subFields[0].tag()

    def _processRawValues(self,dict):
        '''may have to override (usually will) for compound fields... for special app logic'''
        # first, do the process on myself
        SelectField._processRawValues(self,dict)

        # now process the kid
        for each in self._subFields:
            each._processRawValues(dict)

        # figure out what the 'compound' value should be
        self._deriveValue()

    def _deriveValue(self):
        '''figures out what the compound value should be... the value of the set'''
        # if the child's value is anything, use it then grab the value of the box
        try:
            typeinValue = self._subFields[0].value().strip()
        except AttributeError:
            typeinValue = self._subFields[0].value()

        if typeinValue:
            self._derivedValue = typeinValue
        else:
            self._derivedValue = self.nativeValue()


class RadioSetWithOther(CompoundField,RadioSet):

    def __init__(self, name, validators=[], label='', attributes={}, languageStandard='XHTML'):
        RadioSet.__init__(self, name, validators, label, attributes, languageStandard )
        CompoundField.__init__(self, name, validators, label, attributes, languageStandard )

        # add a sub-field that will be the type-in box for the "other" option
        self.addSubField( TextField( self.name()+'_typein', validators=[NotEmpty()]) )

    def tag(self):
        '''cutsom tag method'''
        if not 'other' in self.choiceList().values():
            self.choiceList().addChoice('other','other:')
            if self._derivedValue == 'other':
                self.choiceList().setSelected('other')

        cluster = ''

        for choice in self.choices():
            label = choice.label()
            if choice.value() == 'other':
                label = choice.label() + ' ' + self._subFields[0].tag()
            cluster += '\n%s %s<br%s>' % ( choice.tag(), label, self._closeTag() )

        return cluster

    def _processRawValues(self,dict):
        '''may have to override (usually will) for compound fields... for special app logic'''
        # first, do the process on myself
        RadioSet._processRawValues(self,dict)

        # if it's relevant, process the kids
        if not self.error() and self.nativeValue() =='other':
            for each in self._subFields:
                each._processRawValues(dict)

        # figure out what the 'compound' value should be
        self._deriveValue()

    def _deriveValue(self):
        '''figures out what the compound value should be... the value of the set'''
        # if the main field's value is 'other', then grab the value of the box
        if self.nativeValue() == 'other' and not self.error():
            self._derivedValue = self._subFields[0].value()
        else:
            self._derivedValue = self.nativeValue()