;
(function($) {
  jQuery.fn.populate = function(obj, options) {


    // ------------------------------------------------------------------------------------------
    // JSON conversion function

    // convert
    function parseJSON(obj, path)
    {
      // prepare
      path = path || '';

      // iteration (objects / arrays)
      if (obj == undefined)
      {
      }
      else if (obj.constructor == Object)
      {
        for (var prop in obj)
        {
          var name = path + (path == '' ? prop : '[' + prop + ']');
          parseJSON(obj[prop], name);
        }
      }

      else if (obj.constructor == Array)
        {
          for (var i = 0; i < obj.length; i++)
          {
            var index = options.useIndices ? i : '';
            index = options.phpNaming ? '[' + index + ']' : index;
            var name = path + index;
            parseJSON(obj[i], name);
          }
        }

        // assignment (values)
        else
        {
          // if the element name hasn't yet been defined, create it as a single value
          if (arr[path] == undefined)
          {
            arr[path] = obj;
          }

          // if the element name HAS been defined, but it's a single value, convert to an array and add the new value
          else if (arr[path].constructor != Array)
          {
            arr[path] = [arr[path], obj];
          }

          // if the element name HAS been defined, and is already an array, push the single value on the end of the stack
          else
          {
            arr[path].push(obj);
          }
        }

    }
    ;


    // ------------------------------------------------------------------------------------------
    // population functions

    function debug(str)
    {
      if (window.console && console.log)
      {
        console.log(str);
      }
    }

    function getElementName(name)
    {
      if (!options.phpNaming)
      {
        name = name.replace(/\[\]$/, '');
      }
      return name;
    }

    function populateElement(parentElement, name, value)
    {
      var selector = options.identifier == 'id' ? '#' + name : '[' + options.identifier + '="' + name + '"]';
      var element = jQuery(selector, parentElement);
      value = value.toString();
      value = value == 'null' ? '' : value;
      element.html(value);
    }

    function populateFormElement(form, name, value)
    {

      // check that the named element exists in the form
      var name = getElementName(name); // handle non-php naming
      var element = form[name];

      if (element == undefined)
      {
        debug('No such element as ' + name);
        return false;
      }

      // debug options
      if (options.debug)
      {
        _populate.elements.push(element);
      }

      // now, place any single elements in an array.
      // this is so that the next bit of code (a loop) can treat them the
      // same as any array-elements passed, ie radiobutton or checkox arrays,
      // and the code will just work

      elements = element.type == undefined && element.length ? element : [element];


      // populate the element correctly

      for (var e = 0; e < elements.length; e++)
      {

        element = elements[e];

        switch (element.type || element.tagName) {

          case 'radio':
          // use the single value to check the radio button
            element.checked = (element.value != '' && value.toString().toLowerCase() == element.value.toLowerCase());
            break;

          case 'checkbox':
          // depends on the value.
          // if it's an array, perform a sub loop
          // if it's a value, just do the check

            var values = value.constructor == Array ? value : [value];
            for (var j = 0; j < values.length; j++)
            {
              element.checked |= element.value == values[j];
            }

          //element.checked = (element.value != '' && value.toString().toLowerCase() == element.value.toLowerCase());
            break;

          case 'select-multiple':
            values = value.constructor == Array ? value : [value];
            for (var i = 0; i < element.options.length; i++)
            {
              for (j = 0; j < values.length; j++)
              {
                element.options[i].selected |= element.options[i].value == values[j];
              }
            }
            $(element).trigger('change');
            break;

          case 'select':
            $(element).val(value);
            $(element).trigger('change');
            break;

          case 'select-one':
            $(element).val(value);
            $(element).trigger('change');
          //console.debug($(element).val());
            break;

          case 'text':
          case 'button':
          case 'textarea':
          case 'submit':
          default:
            value = value == null ? '' : value;
            element.value = value;
        }

      }

    }


    // ------------------------------------------------------------------------------------------
    // options & setup

    // exit if no data object supplied
    if (obj === undefined)
    {
      return this;
    }
    ;

    // options
    var options = jQuery.extend
            (
            {
              phpNaming:    true,
              phpIndices:    false,
              resetForm:    true,
              identifier:    'id',
              debug:      false
            },
                    options
                    );

    if (options.phpIndices)
    {
      options.phpNaming = true;
    }

    // ------------------------------------------------------------------------------------------
    // convert hierarchical JSON to flat array

    var arr = [];
    parseJSON(obj);

    if (options.debug)
    {
      _populate =
      {
        arr:    arr,
        obj:    obj,
        elements:  []
      };
    }

    // ------------------------------------------------------------------------------------------
    // main process function

    this.each
            (
                    function()
                    {
                      // variables
                      var tagName = this.tagName.toLowerCase();
                      var method = tagName == 'form' ? populateFormElement : populateElement;

                      // reset form?
                      if (tagName == 'form' && options.resetForm)
                      {
                        this.reset();
                      }

                      // update elements
                      for (var i in arr)
                      {
                        method(this, i, arr[i]);
                      }
                    }
                    );


    return this;
  };
})(jQuery);