"use strict";

var _Object$defineProperty = require("@babel/runtime-corejs3/core-js-stable/object/define-property");
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
_Object$defineProperty(exports, "__esModule", {
  value: true
});
exports.commitServerChanges = commitServerChanges;
exports.defaultState = defaultState;
exports.estimateAttribute = estimateAttribute;
exports.estimateAttributes = estimateAttributes;
exports.mergeFirstPendingState = mergeFirstPendingState;
exports.popPendingState = popPendingState;
exports.pushPendingState = pushPendingState;
exports.setPendingOp = setPendingOp;
exports.setServerData = setServerData;
var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));
var _isArray = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/array/is-array"));
var _keys = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/keys"));
var _some = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/some"));
var _parseInt2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/parse-int"));
var _values = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/values"));
var _stringify = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/json/stringify"));
var _encode = _interopRequireDefault(require("./encode"));
var _CoreManager = _interopRequireDefault(require("./CoreManager"));
var _ParseFile = _interopRequireDefault(require("./ParseFile"));
var _ParseRelation = _interopRequireDefault(require("./ParseRelation"));
var _TaskQueue = _interopRequireDefault(require("./TaskQueue"));
var _ParseOp = require("./ParseOp");
function defaultState() {
  return {
    serverData: {},
    pendingOps: [{}],
    objectCache: {},
    tasks: new _TaskQueue.default(),
    existed: false
  };
}
function setServerData(serverData, attributes) {
  for (const attr in attributes) {
    if (typeof attributes[attr] !== 'undefined') {
      serverData[attr] = attributes[attr];
    } else {
      delete serverData[attr];
    }
  }
}
function setPendingOp(pendingOps, attr, op) {
  const last = pendingOps.length - 1;
  if (op) {
    pendingOps[last][attr] = op;
  } else {
    delete pendingOps[last][attr];
  }
}
function pushPendingState(pendingOps) {
  pendingOps.push({});
}
function popPendingState(pendingOps) {
  const first = pendingOps.shift();
  if (!pendingOps.length) {
    pendingOps[0] = {};
  }
  return first;
}
function mergeFirstPendingState(pendingOps) {
  const first = popPendingState(pendingOps);
  const next = pendingOps[0];
  for (const attr in first) {
    if (next[attr] && first[attr]) {
      const merged = next[attr].mergeWith(first[attr]);
      if (merged) {
        next[attr] = merged;
      }
    } else {
      next[attr] = first[attr];
    }
  }
}
function estimateAttribute(serverData, pendingOps, object, attr) {
  let value = serverData[attr];
  for (let i = 0; i < pendingOps.length; i++) {
    if (pendingOps[i][attr]) {
      if (pendingOps[i][attr] instanceof _ParseOp.RelationOp) {
        if (object.id) {
          value = pendingOps[i][attr].applyTo(value, object, attr);
        }
      } else {
        value = pendingOps[i][attr].applyTo(value);
      }
    }
  }
  return value;
}
function estimateAttributes(serverData, pendingOps, object) {
  const data = {};
  for (var attr in serverData) {
    data[attr] = serverData[attr];
  }
  for (let i = 0; i < pendingOps.length; i++) {
    for (attr in pendingOps[i]) {
      if (pendingOps[i][attr] instanceof _ParseOp.RelationOp) {
        if (object.id) {
          data[attr] = pendingOps[i][attr].applyTo(data[attr], object, attr);
        }
      } else {
        if ((0, _includes.default)(attr).call(attr, '.')) {
          // similar to nestedSet function
          const fields = attr.split('.');
          const last = fields[fields.length - 1];
          let object = data;
          for (let i = 0; i < fields.length - 1; i++) {
            const key = fields[i];
            if (!(key in object)) {
              const nextKey = fields[i + 1];
              if (!isNaN(nextKey)) {
                object[key] = [];
              } else {
                object[key] = {};
              }
            } else {
              if ((0, _isArray.default)(object[key])) {
                object[key] = [...object[key]];
              } else {
                object[key] = {
                  ...object[key]
                };
              }
            }
            object = object[key];
          }
          object[last] = pendingOps[i][attr].applyTo(object[last]);
        } else {
          data[attr] = pendingOps[i][attr].applyTo(data[attr]);
        }
      }
    }
  }
  return data;
}

/**
 * Allows setting properties/variables deep in an object.
 * Converts a.b into { a: { b: value } } for dot notation on Objects
 * Converts a.0.b into { a: [{ b: value }] } for dot notation on Arrays
 *
 * @param obj The object to assign the value to
 * @param key The key to assign. If it's in a deeper path, then use dot notation (`prop1.prop2.prop3`)
 * Note that intermediate object(s) in the nested path are automatically created if they don't exist.
 * @param value The value to assign. If it's an `undefined` then the key is deleted.
 */
function nestedSet(obj, key, value) {
  const paths = key.split('.');
  for (let i = 0; i < paths.length - 1; i++) {
    const path = paths[i];
    if (!(path in obj)) {
      const nextPath = paths[i + 1];
      if (!isNaN(nextPath)) {
        obj[path] = [];
      } else {
        obj[path] = {};
      }
    }
    obj = obj[path];
  }
  if (typeof value === 'undefined') {
    delete obj[paths[paths.length - 1]];
  } else {
    obj[paths[paths.length - 1]] = value;
  }
}
function commitServerChanges(serverData, objectCache, changes) {
  const ParseObject = _CoreManager.default.getParseObject();
  for (const attr in changes) {
    var _context, _context2;
    let val = changes[attr];
    // Check for JSON array { '0': { something }, '1': { something } }
    if (val && typeof val === 'object' && !(0, _isArray.default)(val) && (0, _keys.default)(val).length > 0 && (0, _some.default)(_context = (0, _keys.default)(val)).call(_context, k => !isNaN((0, _parseInt2.default)(k))) && !(0, _includes.default)(_context2 = ['sentPerUTCOffset', 'failedPerUTCOffset']).call(_context2, attr)) {
      val = (0, _values.default)(val);
    }
    nestedSet(serverData, attr, val);
    if (val && typeof val === 'object' && !(val instanceof ParseObject) && !(val instanceof _ParseFile.default) && !(val instanceof _ParseRelation.default)) {
      const json = (0, _encode.default)(val, false, true);
      objectCache[attr] = (0, _stringify.default)(json);
    }
  }
}