"use strict";
/*!
 * Copyright 2018 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
const deepEqual = require('deep-equal');
const validate_1 = require("./validate");
const validate = validate_1.createValidator();
/**
 * Sentinel values that can be used when writing documents with set(), create()
 * or update().
 *
 * @class
 */
class FieldValue {
    /**
     * @private
     * @hideconstructor
     */
    constructor() { }
    /**
     * Returns a sentinel for use with update() or set() with {merge:true} to mark
     * a field for deletion.
     *
     * @returns {FieldValue} The sentinel value to use in your objects.
     *
     * @example
     * let documentRef = firestore.doc('col/doc');
     * let data = { a: 'b', c: 'd' };
     *
     * documentRef.set(data).then(() => {
     *   return documentRef.update({a: Firestore.FieldValue.delete()});
     * }).then(() => {
     *   // Document now only contains { c: 'd' }
     * });
     */
    static delete() {
        return DeleteTransform.DELETE_SENTINEL;
    }
    /**
     * Returns a sentinel used with set(), create() or update() to include a
     * server-generated timestamp in the written data.
     *
     * @return {FieldValue} The FieldValue sentinel for use in a call to set(),
     * create() or update().
     *
     * @example
     * let documentRef = firestore.doc('col/doc');
     *
     * documentRef.set({
     *   time: Firestore.FieldValue.serverTimestamp()
     * }).then(() => {
     *   return documentRef.get();
     * }).then(doc => {
     *   console.log(`Server time set to ${doc.get('time')}`);
     * });
     */
    static serverTimestamp() {
        return ServerTimestampTransform.SERVER_TIMESTAMP_SENTINEL;
    }
    /**
     * Returns a special value that can be used with set(), create() or update()
     * that tells the server to union the given elements with any array value that
     * already exists on the server. Each specified element that doesn't already
     * exist in the array will be added to the end. If the field being modified is
     * not already an array it will be overwritten with an array containing
     * exactly the specified elements.
     *
     * @param {...*} elements The elements to union into the array.
     * @return {FieldValue} The FieldValue sentinel for use in a call to set(),
     * create() or update().
     *
     * @example
     * let documentRef = firestore.doc('col/doc');
     *
     * documentRef.update(
     *   'array', Firestore.FieldValue.arrayUnion('foo')
     * ).then(() => {
     *   return documentRef.get();
     * }).then(doc => {
     *   // doc.get('array') contains field 'foo'
     * });
     */
    static arrayUnion(...elements) {
        validate.minNumberOfArguments('FieldValue.arrayUnion', arguments, 1);
        return new ArrayUnionTransform(elements);
    }
    /**
     * Returns a special value that can be used with set(), create() or update()
     * that tells the server to remove the given elements from any array value
     * that already exists on the server. All instances of each element specified
     * will be removed from the array. If the field being modified is not already
     * an array it will be overwritten with an empty array.
     *
     * @param {...*} elements The elements to remove from the array.
     * @return {FieldValue} The FieldValue sentinel for use in a call to set(),
     * create() or update().
     *
     * @example
     * let documentRef = firestore.doc('col/doc');
     *
     * documentRef.update(
     *   'array', Firestore.FieldValue.arrayRemove('foo')
     * ).then(() => {
     *   return documentRef.get();
     * }).then(doc => {
     *   // doc.get('array') no longer contains field 'foo'
     * });
     */
    static arrayRemove(...elements) {
        validate.minNumberOfArguments('FieldValue.arrayRemove', arguments, 1);
        return new ArrayRemoveTransform(elements);
    }
    /**
     * Returns true if this `FieldValue` is equal to the provided value.
     *
     * @param {*} other The value to compare against.
     * @return {boolean} true if this `FieldValue` is equal to the provided value.
     */
    isEqual(other) {
        return this === other;
    }
}
exports.FieldValue = FieldValue;
/**
 * An internal interface shared by all field transforms.
 *
 * A 'FieldTransform` subclass should implement '.includeInDocumentMask',
 * '.includeInDocumentTransform' and 'toProto' (if '.includeInDocumentTransform'
 * is 'true').
 *
 * @private
 * @abstract
 */
class FieldTransform extends FieldValue {
}
exports.FieldTransform = FieldTransform;
/**
 * A transform that deletes a field from a Firestore document.
 *
 * @private
 */
class DeleteTransform extends FieldTransform {
    constructor() {
        super();
    }
    /**
     * Deletes are included in document masks.
     */
    get includeInDocumentMask() {
        return true;
    }
    /**
     * Deletes are are omitted from document transforms.
     */
    get includeInDocumentTransform() {
        return false;
    }
    get methodName() {
        return 'FieldValue.delete';
    }
    validate() {
        return true;
    }
    toProto(serializer, fieldPath) {
        throw new Error('FieldValue.delete() should not be included in a FieldTransform');
    }
}
/**
 * Sentinel value for a field delete.
 */
DeleteTransform.DELETE_SENTINEL = new DeleteTransform();
exports.DeleteTransform = DeleteTransform;
/**
 * A transform that sets a field to the Firestore server time.
 *
 * @private
 */
class ServerTimestampTransform extends FieldTransform {
    constructor() {
        super();
    }
    /**
     * Server timestamps are omitted from document masks.
     *
     * @private
     */
    get includeInDocumentMask() {
        return false;
    }
    /**
     * Server timestamps are included in document transforms.
     *
     * @private
     */
    get includeInDocumentTransform() {
        return true;
    }
    get methodName() {
        return 'FieldValue.serverTimestamp';
    }
    validate() {
        return true;
    }
    toProto(serializer, fieldPath) {
        return {
            fieldPath: fieldPath.formattedName,
            setToServerValue: 'REQUEST_TIME',
        };
    }
}
/**
 * Sentinel value for a server timestamp.
 *
 * @private
 */
ServerTimestampTransform.SERVER_TIMESTAMP_SENTINEL = new ServerTimestampTransform();
/**
 * Transforms an array value via a union operation.
 *
 * @private
 */
class ArrayUnionTransform extends FieldTransform {
    constructor(elements) {
        super();
        this.elements = elements;
    }
    /**
     * Array transforms are omitted from document masks.
     */
    get includeInDocumentMask() {
        return false;
    }
    /**
     * Array transforms are included in document transforms.
     */
    get includeInDocumentTransform() {
        return true;
    }
    get methodName() {
        return 'FieldValue.arrayUnion';
    }
    validate(validator) {
        let valid = true;
        for (let i = 0; valid && i < this.elements.length; ++i) {
            valid = validator.isArrayElement(i, this.elements[i], { allowDeletes: 'none', allowTransforms: false });
        }
        return valid;
    }
    toProto(serializer, fieldPath) {
        const encodedElements = serializer.encodeValue(this.elements).arrayValue;
        return {
            fieldPath: fieldPath.formattedName,
            appendMissingElements: encodedElements
        };
    }
    isEqual(other) {
        return (this === other ||
            (other instanceof ArrayUnionTransform &&
                deepEqual(this.elements, other.elements, { strict: true })));
    }
}
/**
 * Transforms an array value via a remove operation.
 *
 * @private
 */
class ArrayRemoveTransform extends FieldTransform {
    constructor(elements) {
        super();
        this.elements = elements;
    }
    /**
     * Array transforms are omitted from document masks.
     */
    get includeInDocumentMask() {
        return false;
    }
    /**
     * Array transforms are included in document transforms.
     */
    get includeInDocumentTransform() {
        return true;
    }
    get methodName() {
        return 'FieldValue.arrayRemove';
    }
    validate(validator) {
        let valid = true;
        for (let i = 0; valid && i < this.elements.length; ++i) {
            valid = validator.isArrayElement(i, this.elements[i], { allowDeletes: 'none', allowTransforms: false });
        }
        return valid;
    }
    toProto(serializer, fieldPath) {
        const encodedElements = serializer.encodeValue(this.elements).arrayValue;
        return {
            fieldPath: fieldPath.formattedName,
            removeAllFromArray: encodedElements
        };
    }
    isEqual(other) {
        return (this === other ||
            (other instanceof ArrayRemoveTransform &&
                deepEqual(this.elements, other.elements, { strict: true })));
    }
}
//# sourceMappingURL=field-value.js.map