"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Schema = void 0;
var bigmap_1 = require("../tokens/bigmap");
var createToken_1 = require("../tokens/createToken");
var or_1 = require("../tokens/or");
var pair_1 = require("../tokens/pair");
var token_1 = require("../tokens/token");
var schemaTypeSymbol = Symbol.for('taquito-schema-type-symbol');
// collapse comb pair
function collapse(val, prim) {
    var _b, _c;
    if (prim === void 0) { prim = pair_1.PairToken.prim; }
    if (Array.isArray(val)) {
        return collapse({
            prim: prim,
            args: val,
        }, prim);
    }
    if (val.prim === prim && ((_b = val.args) === null || _b === void 0 ? void 0 : _b.length) > 2) {
        return __assign(__assign({}, val), { args: [val.args[0], {
                    prim: prim,
                    args: (_c = val.args) === null || _c === void 0 ? void 0 : _c.slice(1),
                }] });
    }
    return val;
}
function deepEqual(a, b) {
    var ac = collapse(a);
    var bc = collapse(b);
    return ac.prim === bc.prim &&
        (ac.args === undefined && bc.args === undefined ||
            ac.args !== undefined && bc.args !== undefined &&
                ac.args.length === bc.args.length &&
                ac.args.every(function (v, i) { var _b; return deepEqual(v, (_b = bc.args) === null || _b === void 0 ? void 0 : _b[i]); })) &&
        (ac.annots === undefined && bc.annots === undefined ||
            ac.annots !== undefined && bc.annots !== undefined &&
                ac.annots.length === bc.annots.length &&
                ac.annots.every(function (v, i) { var _b; return v === ((_b = bc.annots) === null || _b === void 0 ? void 0 : _b[i]); }));
}
/**
 * @warn Our current smart contract abstraction feature is currently in preview. It's API is not final, and it may not cover every use case (yet). We will greatly appreciate any feedback on this feature.
 */
var Schema = /** @class */ (function () {
    function Schema(val) {
        this[_a] = true;
        this.root = createToken_1.createToken(val, 0);
        if (this.root instanceof bigmap_1.BigMapToken) {
            this.bigMap = this.root;
        }
        else if (this.isExpressionExtended(val) && val.prim === 'pair') {
            var exp = val.args[0];
            if (this.isExpressionExtended(exp) && exp.prim === 'big_map') {
                this.bigMap = new bigmap_1.BigMapToken(exp, 0, createToken_1.createToken);
            }
        }
    }
    Schema.isSchema = function (obj) {
        return obj && obj[schemaTypeSymbol] === true;
    };
    Schema.fromRPCResponse = function (val) {
        var storage = val &&
            val.script &&
            Array.isArray(val.script.code) &&
            val.script.code.find(function (x) { return x.prim === 'storage'; });
        if (!storage || !Array.isArray(storage.args)) {
            throw new Error('Invalid rpc response passed as arguments');
        }
        return new Schema(storage.args[0]);
    };
    Schema.prototype.isExpressionExtended = function (val) {
        return 'prim' in val && Array.isArray(val.args);
    };
    Schema.prototype.removeTopLevelAnnotation = function (obj) {
        // PairToken and OrToken can have redundant top level annotation in their storage
        if (this.root instanceof pair_1.PairToken || this.root instanceof or_1.OrToken) {
            if (this.root.hasAnnotations() && typeof obj === 'object' && Object.keys(obj).length === 1) {
                return obj[Object.keys(obj)[0]];
            }
        }
        return obj;
    };
    Schema.prototype.Execute = function (val, semantics) {
        var storage = this.root.Execute(val, semantics);
        return this.removeTopLevelAnnotation(storage);
    };
    Schema.prototype.Typecheck = function (val) {
        if (this.root instanceof bigmap_1.BigMapToken && Number.isInteger(Number(val))) {
            return true;
        }
        try {
            this.root.EncodeObject(val);
            return true;
        }
        catch (ex) {
            return false;
        }
    };
    Schema.prototype.ExecuteOnBigMapDiff = function (diff, semantics) {
        if (!this.bigMap) {
            throw new Error('No big map schema');
        }
        if (!Array.isArray(diff)) {
            throw new Error('Invalid big map diff. It must be an array');
        }
        var eltFormat = diff.map(function (_b) {
            var key = _b.key, value = _b.value;
            return ({ args: [key, value] });
        });
        return this.bigMap.Execute(eltFormat, semantics);
    };
    Schema.prototype.ExecuteOnBigMapValue = function (key, semantics) {
        if (!this.bigMap) {
            throw new Error('No big map schema');
        }
        return this.bigMap.ValueSchema.Execute(key, semantics);
    };
    Schema.prototype.EncodeBigMapKey = function (key) {
        if (!this.bigMap) {
            throw new Error('No big map schema');
        }
        try {
            return this.bigMap.KeySchema.ToBigMapKey(key);
        }
        catch (ex) {
            throw new Error('Unable to encode big map key: ' + ex);
        }
    };
    Schema.prototype.Encode = function (_value) {
        try {
            return this.root.EncodeObject(_value);
        }
        catch (ex) {
            if (ex instanceof token_1.TokenValidationError) {
                throw ex;
            }
            throw new Error("Unable to encode storage object. " + ex);
        }
    };
    Schema.prototype.ExtractSchema = function () {
        return this.removeTopLevelAnnotation(this.root.ExtractSchema());
    };
    /**
     * @deprecated
     */
    Schema.prototype.ComputeState = function (tx, state) {
        var _b;
        var _this = this;
        if (!this.bigMap) {
            throw new Error('No big map schema');
        }
        var bigMap = tx.reduce(function (prev, current) {
            return __assign(__assign({}, prev), _this.ExecuteOnBigMapDiff(current.contents[0].metadata.operation_result.big_map_diff));
        }, {});
        return __assign(__assign({}, this.Execute(state)), (_b = {}, _b[this.bigMap.annot()] = bigMap, _b));
    };
    /**
     * @description Look up in top-level pairs of the storage to find a value matching the specified type
     *
     * @returns The first value found that match the type or `undefined` if no value is found
     *
     * @param storage storage to parse to find the value
     * @param valueType type of value to look for
     *
     */
    Schema.prototype.FindFirstInTopLevelPair = function (storage, valueType) {
        return this.findValue(this.root['val'], storage, valueType);
    };
    Schema.prototype.findValue = function (schema, storage, valueToFind) {
        if (deepEqual(valueToFind, schema)) {
            return storage;
        }
        if (Array.isArray(schema) || schema['prim'] === 'pair') {
            var sch = collapse(schema);
            var str = collapse(storage, 'Pair');
            if (sch.args === undefined || str.args === undefined) {
                throw new Error('Tokens have no arguments'); // unlikely
            }
            return this.findValue(sch.args[0], str.args[0], valueToFind) ||
                this.findValue(sch.args[1], str.args[1], valueToFind);
        }
    };
    return Schema;
}());
exports.Schema = Schema;
_a = schemaTypeSymbol;
//# sourceMappingURL=storage.js.map