function Mmf_Form( opts )
{
    var _form, _preventDefaults;

    _init( opts )

    function _init( opts )
    {
        _form = $( opts.formSelector );
        _preventDefaults = ( opts.preventDefaults ) ? opts.preventDefaults : false;

        _MAX_CNT = ( opts.MAX_CNT ) ? opts.MAX_CNT : 4000;
        $( 'textarea[name="message"]', _form ).bind( 'keyup', _characterCount );

        _form.bind( 'submit', _validateAndSend );

        if ( opts.preSelectedSubject
                && opts.preSelectedSubject != '' 
                && $( 'select', _form ) ) {
            _preSelectSubject( opts.preSelectedSubject );
        }

    }

    function _preSelectSubject( subject ) {
        $.each( $( 'select option', _form ),
            function() {
                if ( $( this ).html() == subject
                        || $( this ).val() == subject ) {
                    $( 'select' ).val( $( this ).val() );
                }
            } );
    }

    function _characterCount( event ) {
        var messageLength, error;

        messageLength = event.target.value.length;
        error = $( 'div.message_error' );

        if ( messageLength > _MAX_CNT )
        {
            error.html( 'Your message is too long by ' + ( messageLength - _MAX_CNT ) + ' character(s).' );
            error.css( 'color', '#C22724' );
        }
        else
        {
            error.html( _MAX_CNT - messageLength + ' character(s) left.' );
            error.css( 'color', '' );
        }
    }

    function _validateAndSend( event )
    {
        var validItem, valid;

        event.preventDefault();

        valid = true;

        $.each( $( 'input[type!="hidden"][name], textarea[name], select[name]', _form ),
                function()
                {
                    var validItem;
                    if ( ! _preventDefaults || ! _preventDefaults[ $( this ).attr( 'name' ) ] ) {
                        validItem = eval( '_'
                                        + Mmf_Util.underscoreToTitle( $( this ).attr( 'name' ) )
                                        + 'Validate( $( this ) )' );
                        if ( validItem )
                        {
                            _writeError( $( this ), '' );
                        }
                        valid = valid && validItem;
                    }
                } );
        if ( valid )
        {
            _form.unbind( 'submit' );
            _form.submit();
        }
    }

    function _firstNameValidate( item )
    {
        return ! _isEmpty( item );
    }
    
    function _namesValidate( item )
    {
        return ! _isEmpty( item );
    }

    function _emailsValidate( item )
    {
        var i, emails, valid;

        if ( ! _isEmpty( item ) )
        {
            emails = item.val().split( ',' );
            
            for ( i = 0; i < emails.length; i++ )
            {
                if ( ! _validEmail( Mmf_Util.trim( emails[ i ] ) ) )
                {
                    _writeError( item, 'Invalid email address: ' + emails[ i ] );
                    return false;
                }
            }
            return true;

        }
        else
        {
            return false;
        }
    }

    function _lastNameValidate( item )
    {
        return ! _isEmpty( item );
    }

    function _emailValidate( item )
    {
        return ! _isEmpty( item ) && _validEmail( item );
    }

    function _telephoneValidate( item )
    {
        return ! _isEmpty( item ) && _phoneValidate( item );
    }

    function _subjectValidate( item )
    {
        if ( item.is( 'select' ) )
        {
            return ! _isEmpty( item );
        }
        else
        {
            return true;
        }
    }

    function _messageValidate( item )
    {
        return ! _isEmpty( item ) && _lengthTest( item, _MAX_CNT );
    }

    function _titleValidate( item )
    {
        return ! _isEmpty( item );
    }

    function _companyValidate( item )
    {
        return ! _isEmpty( item )
    }

    function _phoneValidate( item )
    {
        if ( ! /^\d?(\s*|[\-\.])\d{3}(\s*|[\-\.])\d{3}(\s*|[\-\.])\d{4}$/.test( item.val() ) )
        {
            _writeError( item, 'Enter a valid telephone number.' );
            return false;
        }
        return true;
    }


    function _isEmpty( item )
    {
        if ( ! _preventDefaultAction( item, 'isEmpty' ) )
        {
            if ( item.attr( 'value' ) == '' )
            {
                _writeError( item, 'This field is required.' );
                return true;
            }
            else
            {
                return false;
            }
        }
    }

    function _lengthTest( item, lengthLimit )
    {
        if ( ! _preventDefaultAction( item, 'lengthTest' ) )
        {
            if ( item.attr( 'value' ).length > lengthLimit )
            {
                _writeError( item, 
                            'Your input is too long by ' + ( item.val().length - lengthLimit ) + ' character(s).' );
                return false;
            }
            else
            {
                return true;
            }
        }
    }

    function _validEmail( item )
    {
        var str;

        if ( ! _preventDefaultAction( item, 'validEmail' ) )
        {
            str = ( typeof item == 'string' ) ? item : item.val();
            if ( ! /^[A-Za-z0-9\.\_\%\+\-]+\@[A-Za-z0-9\.\-]+\.[A-Za-z]{2,6}$/.test( str ) )
            {
                if ( ! ( typeof item == 'string' ) )
                {
                    _writeError( item, 'Please enter a valid email address.' );
                }
                return false;
            }
            else
            {
                return true;
            }
        }
    }

    function _writeError( item, message )
    {
        var error;

        if ( ( error = $( 'div.error', item.parents( 'tr:first' ) ) ).size() > 0 )
        {
            error.html( message );
        }
        else if ( item.is( 'textarea' ) )
        {
            $( 'div.message_error' ).html( message );
        }
    }

    function _preventDefaultAction( item, name )
    {
        if ( typeof item == 'string' )
        {
            return false;
        }
        return _preventDefaults[ item.attr( 'name' ) ] && _preventDefaults[ item.attr( 'name' ) ][ name ]; 
    }
}

