"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
;
/** Contains information about an error that occurred in ObjectParserProp. */
class ObjectParserError {
    constructor(stack, message) {
        this.stack = stack;
        this.message = message;
    }
    toString() {
        return `${this.message} (stack: "${stackToString(this.stack)}")`;
    }
}
/**
 * A wrapper around a single value (usually a property of an object/array)
 * that makes it easy to iterate over properties of the value, and extract
 * them.
 */
class ObjectParserProp {
    constructor(property, context, stack) {
        this._property = property;
        this._context = context;
        this._stack = stack;
    }
    get value() {
        return this._property;
    }
    map(func) {
        const prop = this._property;
        if (typeof prop === 'object' && prop !== null && !Array.isArray(prop)) {
            for (let label in prop) {
                const item = new ObjectParserProp(prop[label], this._context, createStack(this._stack, label));
                func(item, label);
            }
        }
        else {
            this._context.onError(new ObjectParserError(this._stack, 'Property is not a non-array object.'));
        }
        return this;
    }
    mapRaw(func) {
        const prop = this._property;
        if (typeof prop === 'object' && prop !== null && !Array.isArray(prop)) {
            for (let label in prop) {
                func(prop[label], label);
            }
        }
        else {
            this._context.onError(new ObjectParserError(this._stack, 'Property is not a non-array object.'));
        }
        return this;
    }
    array(func) {
        const prop = this._property;
        if (Array.isArray(prop)) {
            for (let i = 0; i < prop.length; i++) {
                const item = new ObjectParserProp(prop[i], this._context, createStack(this._stack, i));
                func(item, i, prop);
            }
        }
        else {
            this._context.onError(new ObjectParserError(this._stack, 'Property is not an array.'));
        }
        return this;
    }
    arrayRaw(func) {
        const prop = this._property;
        if (Array.isArray(prop)) {
            for (let i = 0; i < prop.length; i++) {
                func(prop[i], i, prop);
            }
        }
        else {
            this._context.onError(new ObjectParserError(this._stack, 'Property is not an array.'));
        }
        return this;
    }
    prop(label, funcOrOptional, optional) {
        // Figure out which argument is used for what purpose
        let func; // ("func" from parameter)
        let isOptional = false; // ("optional" from parameter)
        if (typeof funcOrOptional === 'function') {
            func = funcOrOptional;
            isOptional = !!optional;
        }
        else {
            isOptional = !!funcOrOptional;
        }
        if (this._property !== null &&
            this._property !== undefined &&
            Object.prototype.hasOwnProperty.call(this._property, label)) {
            if (func) {
                func(this._property[label]);
            }
        }
        else if (!isOptional) {
            this._context.onError(new ObjectParserError(this._stack, `Property "${label}" was not found.`));
        }
        const prop = this._property && this._property[label];
        return new ObjectParserProp(prop, this._context, createStack(this._stack, label));
    }
}
class ObjectParser extends ObjectParserProp {
    constructor(options) {
        const context = {
            onError: options.onError || noop
        };
        super(options.input, context, []);
    }
}
exports.ObjectParser = ObjectParser;
/**
 * Copy a label stack and add a label to the end of it.
 * @param stack Stack to add element to.
 * @param label Label to put at the end of the stack.
 */
function createStack(stack, label) {
    const newStack = stack.slice();
    newStack.push(label + '');
    return newStack;
}
/**
 * Create a (pretty) string from a stack.
 * @param stack Stack to create string from.
 */
function stackToString(stack) {
    if (stack.length === 0)
        return '';
    return stack.reduce((prev, cur) => prev + (isArrayIndex(cur) ? `[${cur}]` : `.${cur}`));
}
/**
 * Check if a string is a valid array index (whole number, not NaN, etc.).
 * @param index String to check.
 */
function isArrayIndex(index) {
    return (parseInt(index) + '' === index) && (index !== 'NaN');
}
function noop() { }
//# sourceMappingURL=ObjectParser.js.map