/*
 * $Id$
 * $URL$
 */

/*jslint browser: true, undef: true, evil: false,
    onevar: true, debug: false, on: false, eqeqeq: true */
/*global LMI, YAHOO*/

/**
 * @fileOverview HTML form handling class
 * @name Form
 *
 * @requires url.js
 */

/**
 * HTML form handler
 * @constructor
 * @memberOf LMI
 * @name Form
 *
 * @property {String} action the form's action
 * @property {Object} params form parameters
 * @property {String} method the form's method
 * @property {Function} postCallback method to be called on submit
 */
/** @ignore */
LMI.Form = function(){
    this.action = '';
    this.params = {};
    this.method = 'post';
    this.postCallback = null;
    this.noRefreshUrl = '';
};


LMI.Form.prototype = ( function() {

    return {
        /**
         * Get the form action
         * @name getAction
         * @method
         * @return {String} the form's action
         */
        getAction: function() {
            return this.action;
        },

        /**
         * Set the form action
         * @name setAction
         * @method
         * @param {String} a new action
         */
        setAction: function( a ) {
            this.action = this.parseUrl( a );
        },

        /**
         * Get the form method
         * @name getMethod
         * @method
         * @return {String} the form's method
         */
        getMethod: function() {
            return this.method;
        },

        /**
         * Set the form method
         * @name setMethod
         * @method
         * @param {String} m new method
         */
        setMethod: function( m ) {
            this.method = m;
        },

        /**
         * Set the post callback
         * @name setPostCallback
         * @method
         * @param {Function} callback the new callback
         */
        setPostCallback: function( callback ) {
            this.postCallback = callback;
        },

        /**
         * Remove a variable from the form
         * @name removeVar
         * @method
         * @param {String} name name of the variable to remove
         */
        removeVar: function( name ) {
            if( this.params[name] ) { delete this.params[name]; }
        },

        /**
         * Set a new variable on the form
         * @name setVar
         * @method
         * @param {String} name name of the new variable
         * @param {String} val value of the new variable
         * @param {Boolean} [append] whether or not to append an existing name
         * @param {Boolean} [remove] whether or not to remove the given name
         */
        setVar: function( name, val, append, remove ) {
            var i, cur, added = 0;

            // create the array if it doesn't exist
            if( ! this.params[name] ) { this.params[name] = []; }

            // handle null values
            if( val === null) {
                val = '';
            }

            cur = -1;

            // find the index of the given k/v pair
            for( i=0; i<this.params[name].length; ++i ) {
                if( this.params[name][i] === val ) { cur = i; }
            }

            if( cur > -1 ) {
                if( remove ) {
                    this.params[name].splice( cur, 1 );

                    // remove empty array
                    if( this.params[name].length === 0 ) { delete this.params[name]; }

                    return;
                }
            }

            // only modify an existing-named param if append is set
            if( ! append ) {
                this.params[name] = [];
                this.params[name].push( val );
            }
            added++;

            if( append || !added ) {
                this.params[name].push( val );
            }
        },

        /**
         * Get a variable from the form
         * @name getVar
         * @method
         * @param {String} name name of the variable to get
         * @return {Array} array of values with the given name
         */
        getVar: function( name ) {
            return( this.params[name] ? this.params[name] : [] );
        },

        /* submit constants
         * 0 - normal (default)
         * 1 - norefresh
         * 2 - send sessionid manually
         */
        SUBMIT_NORMAL: 0,
        SUBMIT_NOREFRESH: 1,
        SUBMIT_SESSION: 2,

        /**
         * Returns a populated form object to use in submit()
         * @param {Int} submitType type of submit to perform (use one of the SUBMIT_ constants)
         * @return form object with action and method set, appended to the body element
         */
        createForm: function( submitType ) {
            var i, j,
                body = document.getElementsByTagName( 'body' )[0],
                f = LMI.Element.create( 'form', body, { action:this.action, method:this.method, style:'display:none;position:absolute;top:-4000px;width:0;height:0' } );

            for( i in this.params ) {
                if( this.params.hasOwnProperty( i ) ) {
                    for( j=0; j<this.params[i].length; ++j ) {
                        if( submitType === this.SUBMIT_NOREFRESH ) {
                            // build up a string of param k/v pairs for norefresh use
                            this.noRefreshUrl += i + "=" + this.params[i][j] + "&";

                        } else {
                            LMI.Element.create( 'input', f, { name: i, value: this.params[i][j], style: 'display:none;' } );
                        }
                    }
                }
            }

            return f;
        },

        /**
         * Submit the form
         * @name submit
         * @method
         * @param {Int} submitType type of submit to perform (use one of the SUBMIT_ constants)
         * @return the HTML form object (or the image source, in the case of SUBMIT_NOREFRESH)
         */
        submit: function( submitType ) {
            var img,
                f = this.createForm( submitType );

            /*
             * NOTE: if you get an error "f.submit is not a function" make sure you're
             * never adding a value to the form with a key of "submit"
             * eg: f.setVar( 'submit', 'bob' );
             * also, if you use f.copy( form ), make sure "form" doesn't have an
             * element called "submit"
             */
            if( submitType === this.SUBMIT_NOREFRESH ) {
                // append the sessionId, since IE doesn't send cookies with image requests
                img = document.createElement( 'img' );
                img.src = this.action + ";jsessionid=" + LMI.util.Obj.get( "LMI.Data.state.sessionId" ) + "?" + this.noRefreshUrl + "noresponse=1";
                return img.src;
            } else if( submitType === this.SUBMIT_SESSION ) {
                f.action = this.action + ";jsessionid=" + LMI.util.Obj.get( "LMI.Data.state.sessionId" );
                f.submit();
            } else {
                f.submit();
            }

            return f;
        },

        /**
         * Submit the form
         * @name go
         * @method
         * @param {String} action new action for this submit
         * @param {Int} submitType type of submit to perform (use one of the SUBMIT_ constants)
         * @param {Mixed} [...] name/value pairs of variables to set before submitting
         */
        go: function( action, submitType ) {
            /*
             * dsform.go takes any number of name and val parameters (after action and submitType)
             * now can tage a uri fragment ('#')
             */
            var i, frag;

            for( i=2; i<arguments.length; i++ ) {
                if( arguments[i] === '#' ) {
                    frag = arguments[++i];
                    continue;
                }
                this.setVar( arguments[i], arguments[++i] );
            }

            if( action ) {
                this.setAction( frag ? action + '#' + frag : action );
            }
            this.submit( submitType );
            return false;
        },

        /**
         * Post a given url, first setting it's url parameters as variables on the form
         * @name postUrl
         * @method
         * @param {String} url url to go to
         * @param {Boolean} allowDups whether or not to allow duplicate keys in the url
         */
        postUrl: function( url, allowDups ) {
            var location = this.parseUrl( url, allowDups );
            if( this.postCallback ) {
                location = this.postCallback( location );
            }
            this.go( location );
        },

        /**
         * Parse a given url, setting the parameters as variables on the form
         * @name parseUrl
         * @method
         * @param {String} url url to go to
         * @param {Boolean} allowDups whether or not to allow duplicate keys in the url
         * @return {String} base url
         */
        parseUrl: function( url, allowDups ) {
            var i, j, keys, v,
                u = new LMI.Url( url ),
                location = u.getLocation(),
                ps = u.getParamString();
            if( ps ) {
                location += ';' + ps;
            }
            keys = u.getQueryNames();
            for( i = 0; i < keys.length; ++i ) {
                v = u.getQueryValues( keys[i] );
                for( j = 0; j < v.length; ++j ) {
                    this.setVar( keys[i], v[j], allowDups, false );
                }
            }
            return location;
        },

        /**
         * Post a given url (called as an event handler)
         * @name postLink
         * @method
         * @param {HTMLElement} link the link that needs to be posted
         * @param {Boolean} [allowDups] whether or not to allow duplicate keys in the url
         */
        postLink: function( link, allowDups ) {
            /*
             * parse a the href of a clicked link and submit it as a form
             * with all the other params already in the form
             */
            this.postUrl( link.href, allowDups );

        },

        /**
         * Copy another form's action and variables into this one
         * @name copy
         * @method
         * @param {HTMLElement} form the form to copy from
         */
        copy: function( form ) {
            /*
             * copy the given form's properties into our own form
             */
            if( form.action ) { this.setAction( form.action ); }
            var dsform = this;
            LMI.util.Arr.forEach( form.elements, function( o ) {
                dsform.setVar( o.name, o.value );
            } );
        }
    };
} )();

