/* */ 
"format global";
(function (definition, global) {
  var factory = function (breeze) {
    return definition(breeze, global);
  };

  if (typeof breeze === "object") {
    factory(breeze);
  } else if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
    // CommonJS or Node: hard-coded dependency on "breeze"
    factory(require("breeze"));
  } else if (typeof define === "function" && define["amd"] && !breeze) {
    // AMD anonymous module with hard-coded dependency on "breeze"
    define(["breeze"], factory);
  }
}(function (breeze, global) {
  "use strict";

  var JsonResultsAdapter = breeze.JsonResultsAdapter;
  var AbstractDataServiceAdapter = breeze.AbstractDataServiceAdapter;

  function fmtOData(val) {
    return val == null ? null : "'" + val + "'";
  }

  function getNextObjectId() {
    return new ObjectId().toString();
  }

  var ctor = function DataServiceMongoAdapter() {
    this.name = "mongo";
    breeze.DataType.MongoObjectId = breeze.DataType.addSymbol({
      defaultValue: "",
      fmtOData: fmtOData,
      getNext: getNextObjectId
    });
  };

  var proto = ctor.prototype = new AbstractDataServiceAdapter();

  proto._prepareSaveBundle = function (saveContext, saveBundle) {
    var changeRequestInterceptor = this._createChangeRequestInterceptor(saveContext, saveBundle);
    var em = saveContext.entityManager;
    var metadataStore = em.metadataStore;
    var helper = em.helper;
    var metadata = {};

    saveBundle.entities = saveBundle.entities.map(function (e, ix) {
      var rawEntity = helper.unwrapInstance(e);
      var entityTypeName = e.entityType.name;
      var etInfo = metadata[entityTypeName];
      if (!etInfo) {
        etInfo = {};
        var entityType = e.entityType;
        etInfo.entityTypeName = entityTypeName;
        etInfo.defaultResourceName = entityType.defaultResourceName;
        etInfo.autoGeneratedKeyType = entityType.autoGeneratedKeyType.name;
        etInfo.dataProperties = entityType.dataProperties.map(function (dp) {
          var p = { name: dp.nameOnServer, dataType: dp.dataType.name };
          if (dp.relatedNavigationProperty != null) {
            p.isFk = true;
          }
          if (dp.concurrencyMode && dp.concurrencyMode === "Fixed") {
            p.isConcurrencyProp = true;
          }
          return p;
        });

        metadata[entityTypeName] = etInfo;
        if (!metadata.defaultNamespace) {
          metadata.defaultNamespace = e.entityType.namespace;
        }
      }
      var originalValuesOnServer = helper.unwrapOriginalValues(e, metadataStore);

      rawEntity.entityAspect = {
        entityTypeName: entityTypeName,
        entityState: e.entityAspect.entityState.name,
        originalValuesMap: originalValuesOnServer
      };
      rawEntity = changeRequestInterceptor.getRequest(rawEntity, e, ix);
      return rawEntity;
    });

    saveBundle.metadata = metadata;
    saveBundle.saveOptions = { tag: saveBundle.saveOptions.tag };
    changeRequestInterceptor.done(saveBundle.entities);
    return saveBundle;
  };

  proto._prepareSaveResult = function (saveContext, data) {

    var em = saveContext.entityManager;
    var keys = data.insertedKeys.concat(data.updatedKeys, data.deletedKeys);
    var entities = [];
    keys.forEach(function (key) {
      var entity = em.getEntityByKey(key.entityTypeName, key._id);
      // entities created on the server will not be available via getEntityByKey and hence null;
      if (entity) {
        entities.push(entity);
      }
    });

    if (data.entitiesCreatedOnServer.length > 0) {
      entities = entities.concat(data.entitiesCreatedOnServer);
    }

    return { entities: entities, keyMappings: data.keyMappings, httpResponse: data.httpResponse };
  };


  proto.jsonResultsAdapter = new JsonResultsAdapter({
    name: "mongo",

    visitNode: function (node, mappingContext /*, nodeContext*/) {
      if (node == null) return {};
      var result = {};
      // this will only be set on saveResults and projections.
      if (node.$type) {
        result.entityType = mappingContext.entityManager.metadataStore._getEntityType(node.$type, true);
      }
      return result;
    }
  });

  /*
   *
   * Copyright (c) 2011 Justin Dearing (zippy1981@gmail.com)
   * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
   * and GPL (http://www.opensource.org/licenses/gpl-license.php) version 2 licenses.
   * This software is not distributed under version 3 or later of the GPL.
   *
   * Version 1.0.0
   *
   */

  /*
   * Javascript class that mimics how WCF serializes a object of type MongoDB.Bson.ObjectId
   * and converts between that format and the standard 24 character representation.
   */
  if (global.document) {
    var ObjectId = (function () {
      var increment = 0;
      var pid = Math.floor(Math.random() * (32767));
      var machine = Math.floor(Math.random() * (16777216));

      if (typeof (localStorage) != 'undefined') {
        var mongoMachineId = parseInt(localStorage['mongoMachineId']);
        if (mongoMachineId >= 0 && mongoMachineId <= 16777215) {
          machine = Math.floor(localStorage['mongoMachineId']);
        }
        // Just always stick the value in.
        localStorage['mongoMachineId'] = machine;
        document.cookie = 'mongoMachineId=' + machine + ';expires=Tue, 19 Jan 2038 05:00:00 GMT'
      }
      else if (document) {
        var cookieList = document.cookie.split('; ');
        for (var i in cookieList) {
          var cookie = cookieList[i].split('=');
          if (cookie[0] == 'mongoMachineId' && cookie[1] >= 0 && cookie[1] <= 16777215) {
            machine = cookie[1];
            break;
          }
        }
        document.cookie = 'mongoMachineId=' + machine + ';expires=Tue, 19 Jan 2038 05:00:00 GMT';

      }

      return function () {
        if (!(this instanceof ObjectId)) {
          return new ObjectId(arguments[0], arguments[1], arguments[2], arguments[3]).toString();
        }

        if (typeof (arguments[0]) == 'object') {
          this.timestamp = arguments[0].timestamp;
          this.machine = arguments[0].machine;
          this.pid = arguments[0].pid;
          this.increment = arguments[0].increment;
        }
        else if (typeof (arguments[0]) == 'string' && arguments[0].length == 24) {
          this.timestamp = Number('0x' + arguments[0].substr(0, 8)),
              this.machine = Number('0x' + arguments[0].substr(8, 6)),
              this.pid = Number('0x' + arguments[0].substr(14, 4)),
              this.increment = Number('0x' + arguments[0].substr(18, 6))
        }
        else if (arguments.length == 4 && arguments[0] != null) {
          this.timestamp = arguments[0];
          this.machine = arguments[1];
          this.pid = arguments[2];
          this.increment = arguments[3];
        }
        else {
          this.timestamp = Math.floor(new Date().valueOf() / 1000);
          this.machine = machine;
          this.pid = pid;
          if (increment > 0xffffff) {
            increment = 0;
          }
          this.increment = increment++;

        }
      };
    })();

    ObjectId.prototype.getDate = function () {
      return new Date(this.timestamp * 1000);
    };

    /*
     * Turns a WCF representation of a BSON ObjectId into a 24 character string representation.
     */
    ObjectId.prototype.toString = function () {
      var timestamp = this.timestamp.toString(16);
      var machine = this.machine.toString(16);
      var pid = this.pid.toString(16);
      var increment = this.increment.toString(16);
      return '00000000'.substr(0, 6 - timestamp.length) + timestamp +
          '000000'.substr(0, 6 - machine.length) + machine +
          '0000'.substr(0, 4 - pid.length) + pid +
          '000000'.substr(0, 6 - increment.length) + increment;
    }
  }

  breeze.config.registerAdapter("dataService", ctor);

}, this));