/*jslint indent: 2, maxlen: 80, nomen: true */
/*global document: true, $: true, require: true,  define: true,
 RenderJs: true */
define([
  'overrides',
  'jquery',
  'jqm',
  'extensions',
  'polyfill',
  'text',
  'json',
  'renderjs',
  'i18next',
  'css!../css/jquery-mobile/jquery-mobile.latest',
  'css!../css/css',
  'css!../css/normalize/normalize'
],
  function () {
    "use strict";

    var priv = {},
      that = {};

    /* =================================================================== */
    /*                              ATTRIBUTES                             */
    /* =================================================================== */

    // document
    priv.doc = $.mobile.document || $(document);


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


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

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

          // add a callback to setup to allow hooking into settings to
          // setup page header and footer configuration
          priv.allset.resolve(spec);

  //         .then(function(App, Config) {
  //           console.log(App);
  //           console.log(Config);

          //           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];
          //                 // do all customizations with settings here!
          //                 // modified with replace method
          //                 widget = arguments[i];
          //                 template_target.append( widget );
          //               }
          //             }
          //           );
  //         });


        }
      });
    };

    // ============================ RenderJs related ========================
    //
    // get elements inside a gadget
    // @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 () {
      var root, rootId, setFalse = false, rootUndefined = true;

      // TODO: try to live without all bindings... in renderjs 2
      priv.doc.on('pagebeforeshow', 'div.ui-page', function () {
        var page = $(this),
          pageId = page.attr('id');

        if (rootUndefined) {
          root = RenderJs.GadgetIndex.getRootGadget();
          if (root) {
            rootId = root.id;
            rootUndefined = false;
          }
        }

        // need to call this every time a page is shown...
        // unless we go to the root page, which is always in the GadgetIndex
        if (rootId !== pageId) {
          RenderJs.init(page);

          RenderJs.bindReady(function () {
            var gadget = RenderJs.GadgetIndex.getGadgetById(page.attr('id'));
            if (gadget.isEnhanced() === false) {
              //page.trigger("create");
              gadget.setEnhanced(true);
            }
          });
        }
      })
        .on('pagebeforehide', 'div.ui-page', function (e, data) {
          // pagebeforehide triggers before remove and data includes the new
          // page being loaded. This means we override clearing gadgetIndex
          // in case we are going to a page that has already been loaded.
          // Instead we setReady to false.
          var page = $(this),
            pageId = page.attr('id'),
            nextPageId = data.nextPage.attr('id'),
            pageGadget = RenderJs.GadgetIndex.getGadgetById(pageId);

          if (nextPageId === pageId) {
            // renderJs will try to parse the page before pageremove fires, so
            // if we don't set all gadgets on this page to false, renderJs does
            // nothing...
            if (page.attr("data-gadget") !== undefined) {
              pageGadget.setReady(false);
              pageGadget.setEnhanced(false);
            }
            // also, set all gadgets on this page to false
            setFalse  = true;
          }
        })
        .on('pageremove', 'div.ui-page', function () {
          var page = $(this),
            pageId = page.attr('id'),
            pageGadget;

          // remove gadgets when removing pages,
          // but not if we are doing a same page transition...
          page.find("div[data-gadget]").each(function (i, el) {
            var gadget = $(el);
            if (gadget.is('.main-interactor') === false && !setFalse) {
              RenderJs.GadgetIndex.getGadgetById(gadget.attr('id')).remove();
            }
          });

          // if the page is a gadget itself, it needs to be reset here
          if (page.attr("data-gadget") !== undefined && !setFalse) {
            pageGadget = RenderJs.GadgetIndex.getGadgetById(pageId);
            pageGadget.setReady(false);
            RenderJs.GadgetIndex.unregisterGadget(pageGadget);
          }
          setFalse = false;
        });
    };

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

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

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

      // create new deferred for waiting until all initialization is done
      priv.allset = new $.Deferred();

      priv.allset.done(function (spec) {
        spec.configuration_setter(spec);
      });

      // > set up globals
      priv.setupGlobalParameters();

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

      // 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, forceRefresh) {
      var gadget_id = gadget.dom.attr('id'),
        gadget_parent = gadget.dom.parents("div[data-gadget]"),
        gadget_props,
        gadget_cleaned_id,
        propsToSet,
        new_id,
        fwd = [],
        i;

      if (!gadget_parent.is(".ui-page-active")) {
        return;
      }

      // so now we do it here...
      if (gadget.isEnhanced() === false || forceRefresh) {
        gadget.dom.parent().trigger("create");
        gadget.setEnhanced(true);
      }

      gadget_props = gadget.state;
      gadget_cleaned_id = gadget_id.split("__").slice(-1)[0];

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

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

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

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

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

      // beware: need to get children only to prevent selecting gadgets
      // multiple levels deep
      priv.getElements(gadget, "> div[data-gadget]").each(function () {
        if (fwd.length > 0) {
          this.setAttribute(
            'data-gadget-property',
            JSON.stringify({
              "state": fwd
            })
          );
        }
      });

      // update this gadgets id with the parent gadget id. Make sure to also
      // add the new id inside the renderJs GadgetIndex. Previsouly the id
      // was set only on child gadgets, which worked because gadgets had not
      // been loaded at the time when the id was modified. Since this only
      // seems to work in FF with multiple nested gadget levels, we set the
      // id here, but must also update the id of the gadget in the gadgetIndex
      if (gadget_parent.length > 0) {
        new_id = gadget_parent.attr('id') + "__" + gadget_id;
        RenderJs.GadgetIndex.getGadgetById(gadget_id).id = new_id;
        gadget.dom.attr('id', new_id);
      }
    };

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

    that.settings = priv.settings;

    // RAM cache of items
    that.cacheItems = {};
    that.activeItem = undefined;

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

    // return public methods
    return that;
  }
  );