(function ($) {
    "use strict";
    var timeZone = (new Date()).toTimeString().replace(/.* GMT/, '').replace(/ .*/, '');
    var mapKeysForObject = function (obj) {
        var keys = Object.keys(obj);
        var mapping = {};
        keys.forEach(function (key) {
            mapping[key.toLowerCase()] = key;
        });
        return mapping;
    },

    convertValue = function (value, schema, options) {
        options = options || {};

        if (options.valueConverters && options.valueConverters[value] !== undefined) {
            return options.valueConverters[value];
        }

        // If value object contains a text and value field then..
        if (value.text && value.value) {
            value = value.text;
            schema.fields = [];
        }

        // If value is url then convert it to link using template
        if (schema.isUrl) {
            var template = options.urlHtmlTemplates[schema.urlType];
            var $link = $(template || '<a href></a>');

            $link.prop('href', value);

            if (!template) {
                $link.text(value);
            }

            value = $link[0].outerHTML;
        }

        if (schema.enumDescriptions) {
            if (Array.isArray(value)) {
                value.forEach(function (a, i) {
                    var description = schema.enumDescriptions[a];
                    value[i] = description || a;
                });

                value = value.join(', ');
            } else {
                value = schema.enumDescriptions[value] || value;
            }
        }

        if (schema.postfix) {
            value += schema.postfix;
        }

        switch (schema.convert || "") {
            case "date":
            case "datetime":
                value = value.replace(/T00:00:00\.\d{3,7}Z/, 'T00:00:00.0000000');

                var datetime = kendo.parseDate(value);
                if (datetime.getHours() == 0 && datetime.getMinutes() == 0)
                    value = kendo.toString(datetime, options.kendoDateFormatString);
                else
                    value = kendo.toString(datetime, options.kendoDateTimeFormatString);
                break;
        }

        return value;
    },
      weaveDataSource = function (data, schema, options) {
          var keyMapping = mapKeysForObject(data);
          var result = [];

          schema.fields.forEach(function (field) {
              var memberName = field.memberName.toLowerCase();
              var keyToLookup = keyMapping[memberName];
              var value = data[keyToLookup];

              if (!value && $.inArray(memberName, options.displayNullFields) > -1)
                  value = "";

              if ($.inArray("HideFalsy", field.properties) > -1 && !value)
                  return;

              if ((value || value == "") && !field.hasCategory && $.inArray(memberName, options.forceHideFields) == -1) {
                  result.push({ "Text": field.displayName, "Value": convertValue(value, field, options), "Schema": field });
              }
          });
          return result;
      },

      prioritizeFieldOrder = function (schema, options) {
          var fields = schema.fields;
          
          var orderNo = 1;

          options.priorityFields.forEach(function (v) {
              v = v.toLowerCase();
              for (var k = 0; k < fields.length; k++) {
                  var field = fields[k];
                  if (field.memberName.toLowerCase() === v) {
                      field.orderNo = orderNo++;
                      break;
                  }
              }
          });

          fields.sort(function (a, b) { return (a.orderNo || 999) - (b.orderNo || 999); });
          
          options.lastPriorityFields.forEach(function (v) {
              v = v.toLowerCase();
              for (var k = 0; k < fields.length; k++) {
                  var field = fields[k];
                  if (field.memberName.toLowerCase() === v) {
                      fields.splice(k, 1);
                      fields.push(field);
                      break;
                  }
              }
          });
      },

      generateColumns = function (schema, data) {
          var keyMapping = mapKeysForObject(data || {});
          return schema.fields.map(function (field) {
              var keyToLookup = keyMapping[field.memberName.toLowerCase()];

              return {
                  title: field.displayName,
                  field: keyToLookup,
                  template: '#= '+keyToLookup+' #'
              };
          });
      },
      prepareArrayDataSource = function (array, schema, options) {
          array.forEach(function (data, index) {
              var keyMapping = mapKeysForObject(data);
              schema.fields.forEach(function (field) {
                  var keyToLookup = keyMapping[field.memberName.toLowerCase()];
                  var value = data[keyToLookup];

                  data[keyToLookup] = convertValue(value, field, options);
              });
          });
          return array;
      };

    /*
     * Warning: This plugin will only run on the first matched element
     */
    $.fn.enhancedModelView = function (model, schema, options) {
        if (!model) {
            throw new Error('"model" must be provided');
        }
        if (!schema) {
            throw new Error('"schema" must be provided');
        }

        return this.first().each(function () {
            var $this = $(this),
              enhancedModelViewData = $this.data('enhancedModelView') || {};

            enhancedModelViewData.options = $.extend(true, {}, $.fn.enhancedModelView.defaultOptions, options);

            var $root = $this,
              $tabs = $('<ul />', { 'class': 'nav nav-tabs', 'role': 'tablist' }).appendTo($root),
              $panes = $('<div />', { 'class': 'tab-content' }).appendTo($root),
              compiledGridRowTemplate = kendo.template(enhancedModelViewData.options.template.replace('ROWCLASS', '')),
              compiledGridRowAltTemplate = kendo.template(enhancedModelViewData.options.template.replace('ROWCLASS', 'k-alt')),
              createGrid = function ($container, data, schema, id) {
                  if (!data) {
                      return;
                  }
                  if (!!schema.collection && data && !data.length) {
                      $container.append($('<p />', { 'class': 'text-center' }).text('No data available'));
                  }
                  if (schema.collection == "PropertyList") {
                      // special case
                      return data.map(function (record) {
                          var $pane = $('<div />', {}).css({ 'margin-bottom': '1em' }).appendTo($container);
                          return createGrid($pane, record, $.extend({}, schema, { collection: "" }), id);
                      });
                  }

                  
                  var isArray = !!schema.collection,
                    columns = isArray ? generateColumns(schema, data[0]) : [{ title: "Text", field: "Text", width: 225 }, { title: "Value", field: "Value" }],
                    dataSource = isArray ? prepareArrayDataSource(data, schema, enhancedModelViewData.options) : weaveDataSource(data, schema, enhancedModelViewData.options),
                    options = { rowTemplate: compiledGridRowTemplate, altRowTemplate: compiledGridRowAltTemplate };

                  var grid = $container.kendoGrid($.extend({
                      dataSource: dataSource,
                      sortable: false,
                      scrollable: false,
                      columns: columns,
                      detailInit: isArray ? null : function (e) {
                          if (!e.data.Value && !e.data.Schema || !e.data.Schema.fields.length) return;
                          e.detailRow.children('.k-hierarchy-cell').remove();
                          e.detailCell.attr('colspan', 3).css({ 'padding': '0 0 1em 1em' });
                          if (!createGrid($("<div />").appendTo(e.detailCell).css({ 'borderLeft': 'none', 'borderRight': 'none', 'border-radius': '0' }),
                              e.data.Value,
                              e.data.Schema)) {
                              e.detailRow.remove();
                          };
                      },
                      dataBound: function (e) {
                          this.expandRow(this.tbody.find("tr.k-master-row"));
                      }
                  }, isArray ? {} : options));
                  if (!isArray) {
                      $container.children("table").children(".k-grid-header").remove();
                  }

                  return $container.find("table tr").length > 0;
              },
              
              createTab = function (id, name, data, schema, forceDisplay) {
                  var $pane = $('<div />', { 'class': 'tab-pane', 'id': id }).appendTo($panes);

                  if (createGrid($pane, data, schema, id) || !!enhancedModelViewData.options.empty.display || forceDisplay) {
                      $('<li />').append($('<a />', { 'href': '#' + id, 'title': name, 'role': 'tab' }).attr({ 'data-toggle': 'tab' }).text(name)).appendTo($tabs);
                  };
              },
                
              resolveDisplayName = function(model, name, ref, schema) {
                  var lookup = function(obj, path) {
                      var parts = path.split('.');
                      for (var i = 0, l = parts.length; i < l; i++) {
                          var part = parts[i],
                              partLowerCase = part.toLowerCase(),
                              found = obj[part];
                          if (found === undefined) {
                              var keys = Object.keys(obj).filter(function (x) { return x.toLowerCase() == partLowerCase; });
                              if (keys.length) {
                                  partLowerCase = keys[0];
                                  found = obj[partLowerCase];
                              }
                          }

                          if (schema && schema.fields && schema.fields.length) {
                              var items = schema.fields.filter(function (x) { return x.memberName == part; });
                              schema = items.length ? items[0] : null;
                          }

                          obj = found;
                          if (!obj) return null;
                      }

                      return schema ? convertValue(obj, schema, enhancedModelViewData.options) : obj.match(/[A-Z]+[a-z]+/g).join(" ");
                  };
                  return (ref && model && lookup(model, ref)) || name;
              },

            getOrSetProperty = function(o, s, v) {
                // convert indexes to properties
                s = s.replace(/\[(\w+)\]/g, '.$1').replace(/^\./, '');

                var a = s.split('.');
                while (a.length) {
                    var n = a.shift();
                    if (n in o) {
                        v !== undefined ? o[n] = v : o = o[n];
                    } else {
                        return;
                    }
                }
                return o;
            },
                
            renameTabNames = function (thisSchema, targetModel) {
                targetModel = targetModel || model;
                enhancedModelViewData.options.renamingTabs.forEach(function (v) {
                    var value = getOrSetProperty(targetModel, v.field);

                    if (!(value && thisSchema.memberName == v.tabMemberName)) return;

                    for (var i = 0; i < v.valueNamePairs.length; i++) {
                        if (v.valueNamePairs[i].ifValue == value) {
                            thisSchema.displayName = v.valueNamePairs[i].renameTo;
                            thisSchema.displayNameRef = null;
                            console.log("Renaming Tab", v.tabMemberName, value, thisSchema.displayName);
                            break;
                        }
                    }
                });
            };

            // create tabs -- display name + sections
            var tabs = {},
                keyMapping = mapKeysForObject(model);

            // Rename tab names
            renameTabNames(schema);
            schema.sections.forEach(function (s) { renameTabNames(s, model[keyMapping[s.memberName.toLowerCase()]] || {}); });

            prioritizeFieldOrder(schema, enhancedModelViewData.options);

            tabs[schema.memberName] = { name: resolveDisplayName(model, schema.displayName, schema.displayNameRef), schema: schema };
            createTab(schema.memberName, resolveDisplayName(model, schema.displayName, schema.displayNameRef, schema), model, schema, true);

            (schema.sections || []).forEach(function (section, index) {
                var id = '' + section.memberName + '-' + index,
                    displayName = resolveDisplayName(model, section.displayName, section.displayNameRef, section);

                prioritizeFieldOrder(section, enhancedModelViewData.options);
                tabs[id] = { name: displayName, schema: section };
                createTab(id, displayName, model[keyMapping[section.memberName.toLowerCase()]] || {}, section);
            });

            $(function ($) {
                $('a:first', $tabs).tab('show');
            });

        });
    };

    $.fn.enhancedModelView.defaultOptions = {
        template: '<tr class="k-master-row ROWCLASS"># if ((data.Schema.fields || []).length && data.Value) { #<td class="k-hierarchy-cell" colspan="3"><a href="\\#" class="k-plus"><i class="fa fa-chevron-down"></i>&nbsp;#= data.Text #</a></td># } else { #<td class="field-text" colspan="2" style="font-weight: bold;">#= data.Text #</td><td class="field-value">#= data.Value #</td># } #</tr>',
        kendoDateFormatString: "dd/MM/yyyy",
        kendoDateTimeFormatString: "dd/MM/yyyy h:mm tt",
        empty: {
            display: false
        },
        priorityFields: [],
        lastPriorityFields: []
    };

})(jQuery);
