define([], function () {
  "use strict";

  var priv = {};
  var that = {};

  // ========================================================================
  //                            ATTRIBUTES
  // ========================================================================

  // document
  priv.doc = $.mobile.document;

  // settings > retrieve from JIO later
  priv.settings = {};
  priv.settings.language_default = "en-EN";
  priv.settings.language_current = priv.settings.language_default;
  priv.settings.language_selector = "translate";
  priv.settings.language_set = false;

  // ========================================================================
  //                            FLAGS
  // ========================================================================
  // first page loaded
  priv.pageWrapLoaded;

  // ========================================================================
  //                            INTERNAL METHODS
  // ========================================================================

  //
  // add page header and footer (meta/scripts) to first page loaded (only!)
  // @method wrapFirstPage
  //
  priv.wrapFirstPage = function () {
    // TODO: clean this up
    priv.doc.on('pagecreate.pageWrap', 'div.ui-page', function () {
      if (priv.pageWrapLoaded === undefined) {
        priv.pageWrapLoaded = true;
        priv.doc.off('.pageWrap');

        //           var widgetsForThisRequest = [
        //             "text!../gadgets/pageheader.html",
        //             "text!../gadgets/pagefooter.html"
        //           ],
        //           target = [$('head'),$('body')];
        //
        //
        //           require(
        //             widgetsForThisRequest,
        //             function( /* dynamic */ ) {
        //               var i, widget, template_target;
        //
        //               for ( i = 0; i < widgetsForThisRequest.length; i++){
        //                 template_target = target[i];
        //                 // if customization based on user data required (add logo/img urls, etc)
        //                 // it should be done here with widget being a string that can be
        //                 // modified with replace method
        //                 widget = arguments[i];
        //                 template_target.append( widget );
        //               }
        //             }
        //           );
      }
    });
  };

  // ============================ RenderJs related ========================
  //
  // get elements inside a popup
  // @method getElements
  // @param  {object} gadget to search for recursive gadgets
  //
  priv.getElements = function (gadget, elements) {
    return gadget.dom.find(elements);
  };

  //
  // prefix element id with the parent id to allow instantiation
  // @method prefixId
  // @param  {string} id > of current gadget/element
  // @param  {object} parent gadget
  // @return {string} new id for gadget/element
  //
  priv.prefixElement = function (current_id, parent_id) {
    return parent_id + "__" + current_id;
  };

  //
  // set Attributes passed through data-gadget-properties
  // @method setAttributesOnGadgetElements
  // @param  {object} gadget
  // @param  {properties} properties to set on the gadget
  //
  priv.setAttributesOnGadgetElements = function (gadget, properties) {
    var key, values, i, object, method;

    for (key in properties) {
      if (properties.hasOwnProperty(key)) {
        values = properties[key];

        switch (key) {
          case "_links":
            // TODO: set dictionary here!
          break;
          case "_config":
            // interim: create a fake dictionary
            gadget[key] = values;
          break;
          case "_elements":
            for (i = 0; i < values.length; i += 1) {
              object = values[i];
              method = object[1];
              gadget.dom.find("." + object[0])[method](
                object[2], method === "attr" ? object[3] : null
              );
            }
          break;
        }
      }
    }
  };

  //
  // renderJs initializer and jQuery Mobile handler
  // @method initializeRenderJs
  //
  priv.initializeRenderJs = function () {
    // TODO: remove if possible
    // TODO: try to call "create" earlier (FOUC on first time load)
    priv.doc.on('pagebeforeshow', 'div.ui-page', function () {
      var page = $(this);
      // need to call this every time a page is shown...
      RenderJs.init();

      RenderJs.bindReady(function () {
        var gadget = RenderJs.GadgetIndex.getGadgetById(page.attr('id'));
        if (gadget.isEnhanced() === false) {
          page.trigger("create");
          gadget.setEnhanced(true);
        }
      });
    })
    .on('pageremove', 'div.ui-page', function () {
      var page = $(this),
        pageGadget;

      // if the page is a gadget itself, it needs to be reset here
      if (page.attr("data-gadget") !== undefined) {
        pageGadget = RenderJs.GadgetIndex.getGadgetById(page.attr('id'));
        pageGadget.setReady(false);
        RenderJs.GadgetIndex.unregisterGadget(pageGadget);
      }
      // remove gadgets when removing pages
      page.find("div[data-gadget]").each(function (i, el) {
        var regularGadget = $(el);
        if (regularGadget.is('.main-interactor') === false) {
          RenderJs.GadgetIndex.getGadgetById(regularGadget.attr('id')).remove();
        }
      });
    })
  };

  priv.setScreenDimensions = function () {
    var spec = {};

    // breakpoints should be optionable?
    spec.lowerTresh = 320;
    spec.upperThresh = 720;
    spec.framed = "medium";

    if ($.mobile.media("screen and (max-width:20em)") ||
      ($.mobile.browser.oldIE && $(window).width() < o.lowerThresh)) {
        spec.framed = "small";
    } else if ($.mobile.media("screen and (min-width:45em)") ||
      ($.mobile.browser.oldIE && $(window).width() >= o.upperThresh )) {
        spec.framed = "large";
    }
    priv.settings.screen_format = spec.framed;
  };

  //
  // setup global application parameters
  // @method setupGlobalParameters
  //
  // TODO: this would be the place to load JSON configuration settings
  priv.setupGlobalParameters = function () {

    // screen dimensions
    priv.setScreenDimensions();

    // initialize renderJs
    priv.initializeRenderJs();
  };

  //
  // initialize application
  // @method initialize
  //
  priv.initialize = function () {

    // > set up globals
    priv.setupGlobalParameters()

    // > add <head>er and footer to first page only
    priv.wrapFirstPage();

    // initialize JQM
    // TODO: is there a better way with requireJS/renderJs?
    if ($.mobile.autoInitializePage === false) {
      $.mobile.initializePage();
    }
  };

  // ========================================================================
  //                      PUBLIC METHODS => window.App.method()
  // ========================================================================

  //
  // prefix instance ids and handle/forward JSON properties passed to gadget
  // @method renderGadgets
  // @param {object} gadget to be rendered
  //
  that.renderGadgets = function (gadget) {
    var spec = {};

    spec.gadget_id = gadget.dom.attr('id');
    spec.gadget_props = gadget["state"];
    spec.gadget_cleaned_id = spec.gadget_id.split("__").slice(-1)[0];
    spec.propsToSet;
    spec.fwd = [];
    spec.i;

    // JSON property API
    //  "state": [
    //    {"gadget-id" {"element": ["method", "key/value", "value"] } },
    //    ...
    //  ]
    if (spec.gadget_props !== undefined) {
      for (spec.i = 0; spec.i < spec.gadget_props.length; spec.i += 1) {
        spec.propsToSet = spec.gadget_props[spec.i][spec.gadget_cleaned_id];

        if (spec.propsToSet !== undefined) {
          // properties for this gadget
          priv.setAttributesOnGadgetElements(gadget, spec.propsToSet);
        } else {
          // fwd to new data-gadget-property object
          spec.fwd.push(spec.gadget_props[spec.i]);
        }
      }
      // should we clean up?
      gadget.dom.removeAttr("data-gadget-property");
    }

    priv.getElements(
      gadget, "a[data-rel='popup'], a[data-rel='panel']"
    ).each(function () {
      spec.href = this.getAttribute('href').replace("#", '');
      spec.prefixedHref = priv.prefixElement(spec.href, spec.gadget_id);

      this.setAttribute("href", "#" + spec.prefixedHref);

      // not nice... prevents panel getting prefix__prefix__gadget_id
      // TODO: find better way
      if (this.getAttribute("data-rel") !== "panel") {
        document
          .getElementById(spec.href)
          .setAttribute("id", spec.prefixedHref);
      }
    });

    priv.getElements(gadget, "div[data-gadget]").each(function () {
      spec.id = this.getAttribute('id');
      spec.prefixId = priv.prefixElement(spec.id, spec.gadget_id);
      if (spec.fwd.length > 0) {
        this.setAttribute(
          'data-gadget-property', JSON.stringify({
            "state": spec.fwd
          })
        );
      }
      this.setAttribute('id', spec.prefixId);
    });
  };

  // ========================================================================
  //                            INITIALIZE
  // ========================================================================
  that.initialize = priv.initialize;

  that.settings = priv.settings;

  // ========================================================================
  //                            PUBLISH APP
  // ========================================================================

  // return public methods to main.js
  return that;
});